Mercurial > hg > index.fcgi > lj > lj046-2players
changeset 0:c84446dfb3f5
initial add
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/BUGS.txt Fri Mar 13 00:39:12 2009 -0700 1.3 @@ -0,0 +1,906 @@ 1.4 +bug 0001: Dual Marathon gimmick 1.5 + 1.6 +The keys control both a platform game (in the top window) and a 1.7 +tetromino stacking game (in the bottom window). If you die on 1.8 +either window, you lose. 1.9 + 1.10 + 1.11 +bug 0002: Customizable top out behavior 1.12 +Parity: Tetris (NES), freepuzzlearena 1.13 + 1.14 +A rule can dictate that "top out" occurs when the following happens: 1.15 +* Overlap or lock out: Entry overlapping blocks, or lock entirely 1.16 + above ceiling (current behavior) 1.17 +* Partial lock out: Lock with any block above ceiling 1.18 +* Garbage out: Entry with any block above ceiling 1.19 +* Loose block out: Lock overlapping blocks; block can be shifted 1.20 + out of the way in some cases (like Tetris for NES) 1.21 + 1.22 + 1.23 +bug 0003: Mode 7 style blitter for PC 1.24 +Platform: PC 1.25 +Parity: TOD 1.26 + 1.27 +Extend blitField(LJView *) to scale and rotate the field as it's 1.28 +being copied to the screen. 1.29 + 1.30 + 1.31 +bug 0004: Mode 7 style blitter for GBA and DS 1.32 +Platform: GBA 1.33 + 1.34 +Extend blitField(LJView *) to calculate scaling and rotation factors 1.35 +for each scanline, and extend other functions to start DMA. 1.36 + 1.37 + 1.38 +bug 0005: Port Lockjaw: The Overdose (TOD) 1.39 +Requires: 0002, 0003, 0004 1.40 +Parity: TOD 1.41 + 1.42 +TOD used Carbon Engine. I want to migrate away from Carbon Engine. 1.43 + 1.44 + 1.45 +bug 0006: Ridin' Spinners 1.46 +Requires: 0003, 0004 1.47 +SRS and Infinity are turned on. If you abuse lock delay, or you make 1.48 +a "spin triple" twist, the screen begins to spin, and the music 1.49 +changes to a cover of "Ridin' Spinners" by Three 6 Mafia. 1.50 + 1.51 + 1.52 +bug 0007: Improve Items gimmick 1.53 +Requires: 0008 1.54 +Votes: Caithness 1.55 +Parity: Tetris DS 1.56 + 1.57 +Give only one item at a time in the early stages. 1.58 + 1.59 + 1.60 +bug 0008: Prerotated next queue 1.61 +Parity: Tetris DS 1.62 + 1.63 +Store the orientation of each piece in the next queue. 1.64 + 1.65 + 1.66 +bug 0009: TDS 4-player worldwide simulation 1.67 +Parity: Tetris DS 1.68 + 1.69 +3/4 of the time you start it, it will play annoying music for 1.70 +two minutes, give an 86420 error, and quit. The rest of the 1.71 +time, it will play annoying music for between 15 seconds and 1.72 +two minutes and then switch to Items gimmick. 1.73 + 1.74 + 1.75 +bug 0010: Colored blocks 1.76 +Parity: freepuzzlearena 1.77 + 1.78 +In active, hold, and next, have each block store its color. 1.79 + 1.80 + 1.81 +bug 0011: H&R Blocks scoring method 1.82 +Parity: freepuzzlearena 1.83 + 1.84 +First spotted in freepuzzlearena Tetanus. Each piece is either 1.85 +entirely dark or entirely light. A line of all dark or all light 1.86 +scores as 2 lines. Score for n lines with one piece is n*(n+1)*50. 1.87 + 1.88 + 1.89 +bug 0012: Two physical keys per vkey 1.90 +Platform: PC 1.91 + 1.92 +Allow the player to set more than one key that operates a given, 1.93 +game function, like StepMania does. 1.94 + 1.95 + 1.96 +bug 0013: Two-player mode 1.97 +Parity: freepuzzlearena 1.98 + 1.99 +Allow two players to play at once, and allow garbage to be sent back 1.100 +and forth. 1.101 + 1.102 + 1.103 +bug 0014: Store and display assertion text 1.104 + 1.105 +Lockjaw Engine has ljassert(). If an assertion fails, have some way 1.106 +for front-ends to retrieve the text. 1.107 + 1.108 + 1.109 +bug 0015: Rotation system should be array of structs 1.110 + 1.111 +A single struct should describe all aspects of a rotation system. 1.112 +The descriptions in wktables.c should be an array of structs, not 1.113 +struct of arrays. While you're at it, extend the kick tables 1.114 +from 5 to 8 positions. 1.115 + 1.116 + 1.117 +bug 0016: Rotation system editor 1.118 +Platform: PC 1.119 +Requires: 0015 1.120 +Votes: Needle, DIGITAL, (RA) Red Star, jujube, fnord 1.121 + 1.122 +Before developing Lockjaw Engine, I described Wall Kick Explorer, an 1.123 +editor for rotation systems. Make this. 1.124 +* initial shift and orientation of each piece 1.125 +* kick tables for +90deg and +270deg 1.126 + 1.127 +<tepples> 1.128 +0.44 inverts rotation systems into a format that a loader might be 1.129 +able to produce. 1.130 + 1.131 + 1.132 +bug 0017: Adjustable window size 1.133 +Votes: Bloodstar 1.134 +Resolution: Fixed in 0.42 1.135 + 1.136 +The skin should control the screen resolution or the size of the 1.137 +window. This would make camstudio recording on older PCs feasible. 1.138 + 1.139 + 1.140 +bug 0018: Store keypress count in .ljm 1.141 + 1.142 +Replays should store the number of keypresses, so that Baboo! result 1.143 +screens are correct. 1.144 + 1.145 + 1.146 +bug 0019: Start recording .ljm only during ARE 1.147 + 1.148 +It would probably simplify the code to start a replay only during 1.149 +entry delay. This would also allow playback to become slightly 1.150 +more resilient to game engine changes, as the viewer would at least 1.151 +be able to view block placements. 1.152 + 1.153 + 1.154 +bug 0020: Option for fractional row kick 1.155 +Parity: Tetris DX 1.156 + 1.157 +Right now, a floor kick will move a piece by the minimal amount 1.158 +needed to place a block in the correct row. This means downward 1.159 +kicks move to the top of the row, and upward kicks move to the bottom 1.160 +and immediately start lock delay. Other games don't do this. 1.161 +To simulate Tetris DX properly, we need a new option: 1.162 + 1.163 +Fractional floor kick 1.164 + Unchanged 1.165 +> Minimal move (current behavior) 1.166 + Move to top (Tetris DX behavior) 1.167 + 1.168 + 1.169 +bug 0021: Double buffer entire screen 1.170 +Platform: PC 1.171 +Votes: 4matsy 1.172 + 1.173 +On some video cards, the score flashes. Double buffering the entire 1.174 +screen, not just the well, would fix this. 1.175 + 1.176 + 1.177 +bug 0022: Automatic exit on Vista 1.178 +Platform: PC 1.179 +Resolution: Fixed in 0.42 1.180 + 1.181 +One machine running Windows Vista had Esc perform an immediate exit 1.182 +rather than a pause > exit. 1.183 + 1.184 +<tepples> 1.185 +Might it have been caused by vsync() returning immediately? 1.186 +If so, 0.42 fixes that. 1.187 + 1.188 + 1.189 +bug 0023: Draw internal preview without clipping to well edge 1.190 +Requires: 0020 1.191 +Votes: 4matsy 1.192 + 1.193 +Internal preview should extend outside the well if the piece is close 1.194 +enough to the wall. 1.195 + 1.196 + 1.197 +bug 0024: Improve garbage options 1.198 +Votes: Bloodstar, Lardarse 1.199 +Parity: Tetris (NES) 1.200 + 1.201 +Add option for garbage density, especially for B-type 1.202 + 1.203 +Garbage density 1.204 + Half 1.205 + Two holes 1.206 +> One hole 1.207 + 1.208 +Add option for garbage randomness (1-100) 1.209 + 1.210 + 1.211 +bug 0025: Internet play 1.212 +Requires: 0013 1.213 +Votes: Bloodstar 1.214 +Parity: Tetris DS 1.215 + 1.216 +Allow sending garbage over the Internet. 1.217 + 1.218 + 1.219 +bug 0026: FreeType font support 1.220 +Platform: PC 1.221 +Votes: Bloodstar 1.222 + 1.223 +Use TrueType fonts instead of bitmapped fonts. 1.224 + 1.225 + 1.226 +bug 0027: AVI export 1.227 +Platform: PC 1.228 +Votes: Bloodstar 1.229 +Parity: Tetris (NES) in emulation 1.230 + 1.231 +FCE Ultra and Nestopia can convert a replay to an AVI. 1.232 +Lockjaw should too. 1.233 + 1.234 + 1.235 +bug 0028: Game_Music_Emu music support 1.236 +Platform: PC 1.237 +Votes: Bloodstar 1.238 +Parity: Tetris (NES) in emulation 1.239 + 1.240 +I'd like to play nsf/sid/spc music without having to 1.241 +convert it to .ogg first. 1.242 + 1.243 + 1.244 +bug 0029: Height-sensitive music 1.245 +Platform: PC 1.246 +Votes: Bloodstar 1.247 +Parity: Tetris (NES), Tetris (GB) 1.248 + 1.249 +Tetris DX changes the music when the stack surpasses a certain 1.250 +height. Lockjaw should too. 1.251 + 1.252 + 1.253 +bug 0030: Music on GBA 1.254 +Platform: GBA 1.255 +Votes: Bloodstar 1.256 +Parity: TOD 1.257 + 1.258 +GBA has sound effects. I'd like some sort of music too. Pimpmobile? 1.259 + 1.260 + 1.261 +bug 0031: Debrief on handhelds 1.262 +Platform: GBA, DS 1.263 +Votes: Bloodstar, zzo38computer 1.264 +Resolution: Fixed in 0.43 1.265 + 1.266 +PC has a result screen. I want this on GBA and DS 1.267 + 1.268 +<tepples> 1.269 +Fixed in 0.42 for DS. To get this onto the GBA, we'd have to 1.270 +eliminate three lines of text from the result screen. 1.271 + 1.272 +<tepples> 1.273 +kesiev gave me a few ideas of how I could slim it down. Fixing. 1.274 + 1.275 + 1.276 +bug 0032: Baboo! should force infinite lock delay 1.277 +Votes: caffeine 1.278 + 1.279 +right now, Baboo! is defined by use of 0g. it should be 1.280 +infinite lock delay instead. 1.281 + 1.282 +<tepples> 1.283 +Why? It would add an extra keystroke to Zangi-moves. 1.284 + 1.285 + 1.286 +bug 0033: Move all skin-related files to a folder 1.287 +Platform: PC 1.288 +Votes: caffeine 1.289 +Resolution: Fixed in 0.43 1.290 + 1.291 +Skin-related files should be kept together in the folder "skin". 1.292 + 1.293 + 1.294 +bug 0034: Configure console buttons 1.295 +Platform: PC 1.296 +Votes: caffeine, 4matsy 1.297 +Parity: StepMania 1.298 + 1.299 +Escape to pause, [ to record, ] to play should be configurable. 1.300 + 1.301 +<tepples> 1.302 +4matsy said that MenuUp, MenuDown, MenuLeft, MenuRight, 1.303 +MenuStart, and MenuBack also being configurable might help. 1.304 + 1.305 + 1.306 +bug 0035: skip ready go and game over animations 1.307 +Votes: caffeine 1.308 + 1.309 +I like to kill and restart games when I make one little mistake, 1.310 +and startingAnimation and gameOverAnimation slow me down. 1.311 +I want to skip them, even though each is less than 2 seconds. 1.312 + 1.313 + 1.314 +bug 0036: Skip to mistakes 1.315 +Platform: PC 1.316 +Votes: caffeine 1.317 + 1.318 +While watching a replay, I want to skip to a piece that increases 1.319 +the number of holes or whose active time is greater than three 1.320 +times the average. 1.321 + 1.322 + 1.323 +bug 0037: Hide active times in results 1.324 +Votes: caffeine 1.325 + 1.326 +The Quadra-style "active time" statistics confuse me. I want to 1.327 +see stats based on wall time only. 1.328 + 1.329 + 1.330 +bug 0038: Result time unit 1.331 +Votes: caffeine 1.332 + 1.333 +I'd like to control whether statistics are expressed per minute 1.334 +or per second. 1.335 + 1.336 +<tepples> 1.337 +Why? DDR is measured in beats per minute, and gasoline engine speed 1.338 +is measured in revolutions per minute. 1.339 + 1.340 + 1.341 +bug 0039: More garbage options 1.342 +Votes: Caithness 1.343 + 1.344 +I want more realistic garbage. 1.345 + 1.346 +<tepples> 1.347 +In what way? 1.348 + 1.349 + 1.350 +bug 0040: Custom speed curves 1.351 +Votes: Caithness, caffeine, Ezzelin, zzo38computer, DIGITAL, mushroom 1.352 + 1.353 +Let the user modify speed curves: for example, user should be 1.354 +able to make rhythm speed up slower 1.355 + 1.356 +<tepples> 1.357 +0.45 introduces a new speed curve format 1.358 + 1.359 + 1.360 +bug 0041: Saved rule sets 1.361 +Votes: Needle, kotetsu213, jujube, DIGITAL, Rich Nagel, Deets 1.362 +Votes: Kukuunen 1.363 +Parity: Tetris Worlds, Heboris 1.364 + 1.365 +Let the user create sets of options that override the user's 1.366 +current options, and replace the gimmick selection screen 1.367 +with preset selection. 1.368 + 1.369 +<tepples> 1.370 +0.42 adds scenario support for the PC. A scenario editor and 1.371 +scenario support for handhelds will have to wait. 1.372 + 1.373 + 1.374 +bug 0042: Release Lockjaw XLII 1.375 +Requires: 0041 1.376 + 1.377 +Lockjaw 0.42 won't be released until noticeable progress is made 1.378 +on these issues. 1.379 + 1.380 +<tepples> 1.381 +As of Monday, January 28, 2008, scenario support is in, so Lockjaw 1.382 +will be released the morning before the Super Bowl. 1.383 + 1.384 + 1.385 +bug 0043: More rotation systems 1.386 +Requires: 0016 1.387 +Votes: herc 1.388 + 1.389 +Once the rotation system editor is in place, create these: 1.390 +* Flat-up SRS 1.391 + 1.392 + 1.393 +bug 0044: Vanish zone 1.394 +Platform: PC 1.395 +Votes: DIGITAL 1.396 + 1.397 +The skin should allow showing the vanish zone. 1.398 + 1.399 + 1.400 +bug 0045: Handheld skin support 1.401 +Platform: GBA 1.402 +Votes: Dood77, matt_hatter83 1.403 +Parity: TOD 1.404 + 1.405 +GBA and DS should have skins too. 1.406 + 1.407 + 1.408 +bug 0046: Pussy 20G 1.409 +Votes: herc, DIGITAL 1.410 + 1.411 +Option for moving a piece up 1 cell when shifting it, 1.412 +counting it as a floor kick. 1.413 + 1.414 + 1.415 +bug 0047: Pentominoes 1.416 +Votes: Dood77, jujube, colour_thief, Lardarse 1.417 +Parity: Bombliss 1.418 + 1.419 +Add the 5-block pieces. 1.420 + 1.421 + 1.422 +bug 0048: Bastet randomizer 1.423 +Votes: zzo38computer 1.424 +Parity: Abandoned Blocks 1.425 + 1.426 +In bastet, AI plays each possible next piece in each 1.427 +possible position and gives one of the worst 3. 1.428 + 1.429 + 1.430 +bug 0049: lj-contrib as skins 1.431 +Platform: PC 1.432 +Votes: Rich Nagel 1.433 + 1.434 +lj-contrib was written before .skin files were done. 1.435 + 1.436 + 1.437 +bug 0050: Next above shadow (faint) 1.438 +Platform: PC 1.439 +Votes: cdsboy 1.440 + 1.441 +Draw next above shadow, but draw it semitransparent. 1.442 + 1.443 + 1.444 +bug 0051: Separate keys for initial actions 1.445 +Votes: DIGITAL 1.446 + 1.447 +I like initial actions, but not ARE. Can I get separate 1.448 +keys to perform initial actions on the next piece before 1.449 +this one locks down? 1.450 + 1.451 + 1.452 +bug 0052: Speed graph in debrief 1.453 +Platform: PC 1.454 +Votes: Cubicz 1.455 +Parity: StepMania 1.456 + 1.457 +Screen 1 is results. Screen 2 is settings. 1.458 +Why not put a speed graph on screen 3? 1.459 + 1.460 + 1.461 +bug 0053: High score table 1.462 +Requires: 0041 1.463 +Votes: Cubicz, Ezzelin, Rich Nagel, Kuukunen 1.464 + 1.465 +For each ruleset, keep the highest scores achieved on that 1.466 +ruleset. 1.467 + 1.468 + 1.469 +bug 0054: Disable ljconn 1.470 +Platform: PC 1.471 +Votes: kotetsu213, Needle 1.472 +Parity: Tetris The Grand Master, The New Tetris 1.473 + 1.474 +The skin should be able to turn off ljconn for falling pieces 1.475 +(as opposed to blocks in the stack). That would improve 1.476 +accuracy of TNT and TGM simulation. 1.477 + 1.478 + 1.479 +bug 0055: Skin control for sound effects 1.480 +Platform: PC 1.481 +Votes: Cubicz, Needle 1.482 +Parity: Lumines 1.483 + 1.484 +Allow skins to specify a .dat file or set of .wav files used 1.485 +for sound effects. 1.486 + 1.487 + 1.488 +bug 0056: Bleep gimmick 1.489 +Votes: Lardarse 1.490 + 1.491 +Player must clear row 1, 2, 1, 3, 1, 4, 1, 5, 1, ..., 20, 1. 1.492 + 1.493 + 1.494 +bug 0057: Bravo 1.495 +Votes: Lardarse 1.496 +Parity: Luminesweeper 1.497 + 1.498 +Several other falling block games display "BRAVO!" or another similar 1.499 +message when there are no blocks left in the well after a line clear, 1.500 +and award lots of bonus points. Detect and score for this situation. 1.501 + 1.502 + 1.503 +bug 0058: Vs. Elite garbage 1.504 +Votes: PetitPrince 1.505 + 1.506 +you get 7 garbages after random(1,7) seconds, then random(3,5) 1.507 +garbage every random (5,20) seconds 1.508 + 1.509 + 1.510 +bug 0059: Wall kick control for IRS 1.511 +Votes: jagorochi 1.512 +Parity: TGM 1.513 + 1.514 +In TGM, initial rotation does not perform kicks. 1.515 +Change the IRS option: 1.516 + 1.517 +Initial rotation 1.518 + Off: do not IRS (current behavior) 1.519 + On, no kick: if collision, fail 1.520 +> On: if collision, try wall kick (current behavior) 1.521 + 1.522 +<tepples> 1.523 +0.43 disallows IRS kick entirely, changing the current behavior 1.524 +from "On" to "On, no kick", by adding a new argument to 1.525 +doRotateLeft and doRotateRight. The change to this option (search 1.526 +for !isFirstFrame) could be used to implement this option. 1.527 + 1.528 + 1.529 +bug 0060: Menu background 1.530 +Platform: PC 1.531 +Votes: lvankeulen, Kuukunen 1.532 +Parity: Tetris (NES) 1.533 + 1.534 +The skin should specify a background image to be drawn behind 1.535 +the menu text. 1.536 + 1.537 + 1.538 +bug 0061: Frame stepping 1.539 +Platform: PC 1.540 +Votes: jagorochi 1.541 +Parity: Tetris (NES) in emulation 1.542 + 1.543 +To check the event loop for off-by-one errors that affect 1.544 +accuracy against other games, it would be useful to be able 1.545 +to step the game engine forward by one frame at a time. 1.546 + 1.547 + 1.548 +bug 0062: Replay stepping by piece 1.549 +Platform: PC 1.550 +Votes: Lardarse 1.551 + 1.552 +Pause demo playback (with well still visible) with hold button; 1.553 +advance to next lock with rotate button 1.554 + 1.555 + 1.556 +bug 0063: Option for goal 1.557 +Votes: Lardarse, caffeine, Rosti LFC, fnord, M.Bison 1.558 + 1.559 +Separate goal and countdown code out of gimmicks into an option, 1.560 +controlled by LJField::goalType. Goals include none, lines, 1.561 +level, score, time, and keys, which activate comparisons against 1.562 +goalCount. This would obsolete many gimmicks. 1.563 + 1.564 + 1.565 +bug 0064: Fill in missing sound effects on handhelds 1.566 +Platform: GBA, DS 1.567 +Votes: Lardarse 1.568 + 1.569 +These sounds are missing: 1.570 +GBA: Ready, go, countdown 1.571 +DS: Ready, go, lose, distinct countdown 1.572 + 1.573 +<tepples> 1.574 +0.42 adds "lose" on DS 1.575 + 1.576 + 1.577 +bug 0065: Bombliss game mode 1.578 +Requires: 0010 1.579 +Votes: Joshua 1.580 +Parity: Bombliss 1.581 + 1.582 +When game mode is set to Bombliss, line clears no longer remove 1.583 +lines from the field. Instead, one block of each piece is 1.584 +colored differently and explodes, removing blocks, when in a 1.585 +line clear. 1.586 + 1.587 + 1.588 +bug 0066: Dr. Mario clone 1.589 +Requires: 0010 1.590 +Votes: Lardarse 1.591 +Parity: freepuzzlearena 1.592 + 1.593 +You made Vitamins. Can you make domino rotation, color matching, 1.594 +and floating garbage with fast DAS, IRS, and 20G? 1.595 + 1.596 + 1.597 +bug 0067: Puyo clone 1.598 +Requires: 0010 1.599 +Votes: Lardarse 1.600 +Parity: freepuzzlearena 1.601 + 1.602 +You made a puyo clone in FPA. Can you port it to LJ? 1.603 + 1.604 + 1.605 +bug 0068: Preload hold piece 1.606 +Votes: Lardarse 1.607 + 1.608 +Option to load a specific piece (such as I or T) into the hold box 1.609 +at game start. 1.610 + 1.611 + 1.612 +bug 0069: Bag with good start 1.613 +Votes: Rosti LFC 1.614 +Parity: Tetris The Grand Master ACE 1.615 + 1.616 +Randomizer should always give a "preferred piece" (e.g. I, J, L, T) 1.617 +first, like history does. 1.618 + 1.619 + 1.620 +bug 0070: Small skins shouldn't hide the score 1.621 +Votes: Rich Nagel 1.622 +Platform: PC 1.623 + 1.624 +Make wiki.skin (and other small-blkW skins) not hide the score 1.625 + 1.626 + 1.627 +bug 0071: Preview at left 1.628 +Votes: DIGITAL 1.629 +Platform: PC 1.630 + 1.631 +Skin should specify that the vertical preview ("Next at right") 1.632 +can be moved to the left side of the playfield. 1.633 + 1.634 + 1.635 +bug 0072: Volume for sfx 1.636 +Votes: DIGITAL 1.637 +Platform: PC 1.638 + 1.639 +Skin should specify volume for sound effects, as it does for music. 1.640 + 1.641 + 1.642 +bug 0073: Make PC use GBA options 1.643 +Votes: jujube 1.644 +Platform: PC 1.645 +Resolution: Fixed in 0.43 1.646 + 1.647 +Move non-GBA/DS specific code from gbaopt.c to options.c, 1.648 +then take out everything in old_pc_options.c that isn't 1.649 +load, save, or LJPCView related 1.650 + 1.651 +<tepples> 1.652 +0.42 takes some steps in this direction. 1.653 + 1.654 + 1.655 +bug 0074: Macro editor 1.656 +Votes: deepdorp 1.657 + 1.658 +Make the actions performed by the macro vkeys editable. 1.659 + 1.660 + 1.661 +bug 0075: Pattern randomizer 1.662 +Votes: Rich Nagel 1.663 + 1.664 +Allow the player to define a piece sequence, such as the Sega 1.665 +power-on pattern, and deal that. It could replace iCheat(tm). 1.666 + 1.667 + 1.668 +bug 0076: Skin should vary per section 1.669 +Votes: Needle, Rich Nagel, DIGITAL 1.670 +Parity: freepuzzlearena, Luminesweeper 1.671 + 1.672 +For speed curves that have sections, it should be possible for a 1.673 +skin to specify a background image and music for each section. 1.674 + 1.675 +<Rich Nagel> 1.676 +Option to load all skin files before the game starts, so as not to 1.677 +make an unpredictable ARE at the start of a section. 1.678 + 1.679 + 1.680 +bug 0077: Define conditions that trigger initial drop 1.681 +Votes: Needle 1.682 +Parity: Tetris The Absolute The Grand Master 2 PLUS 1.683 + 1.684 +In TAP, initial hard drop and initial soft drop don't get turned on 1.685 +in Master 900+ or in Death 0+. Find an appropriate rule for whether 1.686 +to do initial hard drop, and document it. 1.687 + 1.688 + 1.689 +bug 0078: Set random seed 1.690 +Votes: Rich Nagel, colour_thief, Lardarse 1.691 +Parity: FreeCell 1.692 + 1.693 +For competition purposes, it should be possible to seed the 1.694 +randomizer to a constant value. Note that this differs from the 1.695 +"power on pattern" proposal of bug 0075. 1.696 + 1.697 + 1.698 +bug 0079: Customizable speedometer sample window 1.699 +Votes: matt_hatter83 1.700 + 1.701 +The speedometer uses a sliding window of 10 pieces. Can one make it 1.702 +shorter in options? 1.703 + 1.704 + 1.705 +bug 0080: Portrait monitor support 1.706 +Votes: PetitPrince 1.707 + 1.708 +Skin setting to rotate the entire playfield by 90 degrees. 1.709 + 1.710 + 1.711 +bug 0081: DTET rotation system 1.712 +Requires: 0015 1.713 +Votes: cdsboy, caffeine, fnord 1.714 +Parity: DTET 1.715 + 1.716 +Implement the rotation system of DTET, as described on the wiki. 1.717 +This includes a third set of wall kick tables for each piece in each 1.718 +rotation system for +180 (in addition to existing +90 and +270). 1.719 + 1.720 + 1.721 +bug 0082: Paged options on PC 1.722 +Votes: Cubicz 1.723 +Resolution: Fixed in 0.43 1.724 + 1.725 +Break the PC options menu into pages like it is on the handhelds. 1.726 + 1.727 + 1.728 +bug 0083: Undo 1.729 +Votes: Kuukunen 1.730 + 1.731 +Press a button to take back one drop. Useful for training. 1.732 + 1.733 + 1.734 +bug 0084: Manual entry delay 1.735 +Votes: Kuukunen 1.736 + 1.737 +Pause after each piece with game displayed. Press a button to 1.738 +resume. Useful for training. 1.739 + 1.740 + 1.741 +bug 0085: Forward progress lock delay reset 1.742 +Votes: zaphod77 1.743 + 1.744 +Keep track of the lowest space that this piece has fallen to. 1.745 +A floor kick should never reset lock delay, and only a net 1.746 +downward movement below where it was when the lock delay was 1.747 +last reset should reset lock delay. 1.748 + 1.749 + 1.750 +bug 0086: Carbon Engine style lock delay reset 1.751 +Parity: TOD 1.752 + 1.753 +Carbon Engine implemented lock delay as follows: When a piece 1.754 +rotates or shifts from landed to falling, recharge one row's 1.755 +worth of lock delay for each row that the piece moves down. 1.756 + 1.757 +To implement this in Lockjaw Engine, move the piece down by 1.758 +(lockDelay - stateTime). If the piece is less than 1.759 +(lockDelay - stateTime) * gravity above the shadow, only add 1.760 +(y - shadowY) / gravity to stateTime. 1.761 + 1.762 + 1.763 +bug 0087: Blackjack randomizer framework 1.764 +Votes: Lardarse 1.765 + 1.766 +Lardarse and tepples described a language for describing randomizers 1.767 +on tetrisconcept.com's wiki. It is called Blackjack. 1.768 + 1.769 + 1.770 +bug 0088: Option for piece color 1.771 +Votes: Lardarse 1.772 +Requires: 0010 1.773 + 1.774 +Once we have colors per block, add an option for piece colors, 1.775 +which might replace the ljconnSRS vs. ljconnSega divide. 1.776 +Skins would always be in Guideline order. 1.777 + 1.778 +> Set by rotation system (current behavior) 1.779 + SRS 1.780 + Sega 1.781 + Dualism (formerly H&R) 1.782 + Shuffle (randomize at game start) 1.783 + Super Shuffle (randomize every piece) 1.784 + 1.785 + 1.786 +bug 0089: Long debrief lines are clipped 1.787 +Requires: 0031 1.788 +Votes: Lardarse, kesiev 1.789 +Resolution: Fixed in 0.43 1.790 + 1.791 +The DS doesn't have a small font, so any debrief line longer than 1.792 +about 48 characters will get clipped. But in order to make this 1.793 +line shorter, I need to move things around anyway. 1.794 + 1.795 + 1.796 +bug 0089: Merge preliminary GNU/Linux support 1.797 +Requires: 0091 1.798 +Votes: kesiev 1.799 +Resolution: Fixed in 0.43 1.800 + 1.801 +kesiev wrote a patch to let Lockjaw work with separate read-only 1.802 +and read-write folders, as is often the case in GNU/Linux. 1.803 +Merge these changes. 1.804 + 1.805 +<tepples> 1.806 +kesiev's patch adds a function called 'turnslash' that turns '\' into 1.807 +'/' in all path names. In fact, I can use this on all platforms: 1.808 +Mac and Linux use '/' as the native path separator, and the Windows 1.809 +API accepts it even if some legacy command-line programs do not. 1.810 + 1.811 +LJVorbis_close: I must have missed that line in the ov_clear 1.812 +documentation. 1.813 + 1.814 + 1.815 +bug 0090: Process IRS before checking for block out 1.816 +Votes: Kitaru 1.817 +Parity: TGM series 1.818 +Resolution: Fixed in 0.43 1.819 + 1.820 +TGM allows the player to IRS out of a block out situation. So don't check for block out on the last frame of NEW_PIECE. Instead, check at 1.821 +the end of the first frame (LJSND_SPAWN|LJSND_HOLD) of FALLING_PIECE. 1.822 + 1.823 + 1.824 +bug 0091: Read-only vs. read-write directories 1.825 +Votes: Ezzelin, kesiev 1.826 +Resolution: Fixed in 0.43 1.827 + 1.828 +Lockjaw for PC currently runs as a "portable application". But it 1.829 +should support being installed in the same way as any other program 1.830 +for Windows or Linux. If an empty file called "installed" is in the 1.831 +same folder as argv[0], switch to installed mode. 1.832 + 1.833 +Installed mode ignores the working directory when loading .dat, 1.834 +.skin, .ini, etc. Instead, it loads from the save data folder 1.835 +($HOME/.lockjaw on Linux; %APPDATA%/Pin Eight/Lockjaw on Windows) 1.836 +and then from the folder that contains argv[0]. 1.837 + 1.838 +The program would usually be installed to /opt/Lockjaw under Linux 1.839 +or %ProgramFiles%/Lockjaw under Windows. 1.840 + 1.841 + 1.842 +bug 0092: Option to limit IRS to 90 degrees 1.843 +Votes: jujube 1.844 +Parity: TGM series 1.845 + 1.846 +Rotation limit 1.847 + 90 degrees 1.848 +> Unlimited (current behavior) 1.849 + 1.850 +And make Master and Death use 90 degrees. 1.851 + 1.852 + 1.853 +bug 0093: Option to disable instant shifting 1.854 +Parity: TGM series 1.855 + 1.856 +Add an option only to scenarios that limits shifting to 1G, 1.857 +disallowing cheating with far left, far right, DAS=Instant. 1.858 + 1.859 + 1.860 +bug 0094: World reverse 1.861 +Votes: jujube 1.862 +Parity: Ti 1.863 + 1.864 +Option in Game Keys to reverse rotation directions when playing 1.865 +rotation systems that start the T flat-down (SRS, TOD M4). 1.866 + 1.867 + 1.868 +bug 0095: Move mouse pointer out of the way 1.869 +Votes: jujube 1.870 +Resolution: Fixed in 0.43 1.871 + 1.872 +Some players navigate Lockjaw for PC entirely with the keyboard. 1.873 +For these people, the mouse gets in the way after it has been used 1.874 +in Replay or Skin (or, soon, Options and Game Keys). Drop it to 1.875 +the bottom of the window when the player goes to a screen that 1.876 +doesn't use the mouse. 1.877 + 1.878 + 1.879 +bug 0096: Allow options to "underride" skin settings 1.880 +Votes: Rich Nagel 1.881 + 1.882 +Allow the user to change shift sound scale, next piece position, 1.883 +playfield position, as it was in 0.41. The game would use the 1.884 +skin setting, or the user setting only if the skin does not specify. 1.885 + 1.886 + 1.887 +bug 0097: Package as Windows installer 1.888 +Requires: 0091 1.889 + 1.890 +Package the Windows executable as an installer that automatically 1.891 +puts itself in %ProgramFiles%/Lockjaw. 1.892 + 1.893 + 1.894 +bug 0098: Reactive speed curve and scoring 1.895 +URL: http://www.jenovachen.com/flowingames/flowtheory.htm 1.896 +Votes: herc 1.897 + 1.898 +Start with zero speed. Track the player's speed using the existing 1.899 +speedometer code. Then once the player has dropped a few pieces, 1.900 +start scaling up the delays and such so that entry + (20/gravity) + 1.901 +lock delay equals the average time that the player took to place 1.902 +a piece. Then multiply all line clear scores by the current speed. 1.903 + 1.904 + 1.905 +bug 0100: Import old bugs 1.906 +Parity: bugzilla.mozilla.org 1.907 + 1.908 +Bring some order to the old 'todo.txt' enhancement list. 1.909 +
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/CHANGES.txt Fri Mar 13 00:39:12 2009 -0700 2.3 @@ -0,0 +1,1067 @@ 2.4 +0.46a (2008-11-09) 2.5 + * Preview at top is copied correctly from the back buffer 2.6 + (0.45a regression, reported by rednefed). 2.7 + 2.8 +_____________________________________________________________________ 2.9 +0.46 (2008-11-08) 2.10 + * Tracks number of 4x4 squares that the player forms 2.11 + (TOD parity; requested by Ghett0). 2.12 + * Added T-Party randomizer (requested by Lardarse). 2.13 + * Default lock delay for Zero set to 40 frames, even if it is 2.14 + played first (0.45 regression, reported by rck). 2.15 + * "[TC]" removed from scenario names (requested by caffeine). 2.16 + * PC: Uses fourCCs for option values in lj.ini. 2.17 + * PC: Uses double buffer for entire background (bug 0021). 2.18 + * PC: Can disable sound effects by deleting or renaming sound.dat 2.19 + (requested by moxie101). 2.20 + * PC: More of the code is 2-player-clean. 2.21 + 2.22 +_____________________________________________________________________ 2.23 +0.45 (2008-04-20) 2.24 + * New unified speed curve system: less code, more tables, for 2.25 + future loading from a file (bug 0040). 2.26 + * All speed curves except Rhythm and Rhythm Zero have been ported 2.27 + to the new system. Notably, Master has been rewritten based on 2.28 + the tables from the wiki, and Exponential uses sections. The 2.29 + lock delay speeds up as brutally after 600 pieces as always. 2.30 + * Added kludge in the basic makefile that allows building on *BSD 2.31 + and GNU/Linux: 'make linux' (patch by kesiev). 2.32 + * ljpath: Fixed a couple misspellings in the non-Windows code path 2.33 + (reported by kesiev). 2.34 + * ljpath: Supports libfat when running on Nintendo DS. 2.35 + * A few descriptions of options have been shortened to fit into 2.36 + the new GBA/DS look (reported by Bloodstar; patch by caffeine). 2.37 + * Writes result and options text to standard output as well as to 2.38 + lj-scores.txt. This allows you to do 'lj | postprocessor' even 2.39 + if you can't do 'tail -f lj-scores.txt' (requested by kesiev). 2.40 + * DS: Writes result and options to /data/lockjaw/lj-scores.txt. 2.41 + 2.42 +_____________________________________________________________________ 2.43 +0.44 (2008-02-20) 2.44 + * Computes the preview and hold offset of all pieces (was 2.45 + wkNextMove in <= 0.43) from the entry position of the I 2.46 + tetromino (was wkSpawnMove in <= 0.43). 2.47 + * Kick tables have been inverted to a single struct per rotation 2.48 + system, for future loading from a file (bug 0016). 2.49 + * SKIP_IF system for handling J/L/T exceptions in Arika is replaced 2.50 + with IF_NOT_CENTER: wall kicks are allowed if the free-space 2.51 + orientation overlaps any blocks other than in the center column. 2.52 + * Options DAS doesn't repeat too fast, even on those systems where 2.53 + vsync() is a no-op (0.43 regression, reported by caffeine). 2.54 + This has the same cause as 0022: Windows Vista ignores vsync(). 2.55 + * Options DAS doesn't continue across page boundaries. 2.56 + * GBA/DS: Options has new look. 2.57 + * DS: Reads the touch screen and the X and Y buttons. Right now, 2.58 + touch isn't mapped to anything, but X and Y are Far Right and 2.59 + Far Left. 2.60 + 2.61 +_____________________________________________________________________ 2.62 +0.43a (2008-02-11) 2.63 + * Arika modified to use SKIP_IF3 after (not before) the basic 2.64 + position. This restores ability to rotate J, L, and T with 2.65 + kicks turned off, such as in IRS (reported by Lion). 2.66 + * PC: Uses shfolder.dll so that installability works even on 2.67 + Windows 98 (0.43 regression, reported by Rich Nagel). 2.68 + * PC: Key bindings to J, M, and Z are correctly saved (0.43 2.69 + regression, reported by DIGITAL). 2.70 + * PC: In Game Keys, the names of Up and Down key bindings are 2.71 + clearer (requested by 4matsy). 2.72 + 2.73 +_____________________________________________________________________ 2.74 +0.43 (2008-02-10) 2.75 + * Player can initial-rotate out of block out (bug 0090). 2.76 + * Initial rotation doesn't use wall kick (bug 0059). 2.77 + * Names for values in Drop scoring and Shadow fixed 2.78 + (0.42 regression, reported by Lardarse). 2.79 + * Number of line clears in DS debrief should be cut off at the 2.80 + right side less often (bug 0089; fix based on a patch by kesiev). 2.81 + * Fixed line clear gravity in cascade preset (reported by 4matsy). 2.82 + * Fixed Fibonacci scoring with more than 8 lines per piece 2.83 + (reported by 4matsy). 2.84 + * Debrief is more compact, allowing GBA to use debrief (bug 0031; 2.85 + fix based on a patch by kesiev). 2.86 + * Debrief: Zigzag secret grade requires the hole in the top row to 2.87 + be covered (reported by Kitaru). 2.88 + * PC: If installed.txt is in the same folder as the program, 2.89 + ignores the current directory and stores all writable files 2.90 + to the user's application data folder (bug 0091). 2.91 + * PC: When loading skins, searches for files in the folder with 2.92 + the .skin file instead of the current directory (bug 0033). 2.93 + * PC: Replay sets up playback gimmick (0.42 regression, 2.94 + reported by DIGITAL). 2.95 + * PC: LJVorbis doesn't double-fclose the .ogg file 2.96 + (reported by kesiev). 2.97 + * PC: Mouse drops to bottom after skin or replay selection 2.98 + (requested by jujube). 2.99 + * PC: Options is separated into pages, like on GBA/DS (bug 0082). 2.100 + This frees up room for larger type (requested by jujube). 2.101 + 2.102 +_____________________________________________________________________ 2.103 +0.42 (2008-02-03) aka LOCKJAW XLII 2.104 + * Docs: Most of TODO.txt has been converted to a numbered list 2.105 + of bugs (bug 0100). 2.106 + * An off-by-1/65536 error in enter below ceiling was fixed. 2.107 + This means it doesn't give one extra row of room to slide on 2.108 + at the top of the well (reported by Caithness and Kukuunen). 2.109 + * In case of a block out, draws the offending piece in the well 2.110 + before entering game over animation. 2.111 + * Extra 5-frame cascade delay removed. 2.112 + * With Lockdown set to Step reset, lock delay resets on the 2.113 + first floor kick. 2.114 + * Tengen rotation system tries kicking one space to the left 2.115 + (requested by zaphod77). 2.116 + * Options' names and values are looked up from fourCCs for future 2.117 + localizability. 2.118 + * Options: Names of speeds in fractions of 1G are computed. 2.119 + * Options skips unpacking options with a value of 255. 2.120 + This allows multiple pref structures to be unpacked on top 2.121 + of each other, and each overrides the ones before it. 2.122 + * Debrief displays options on a second page. 2.123 + * Debrief correctly displays soft drop speed as a fraction (0.41 2.124 + regression, reported by Lardarse). 2.125 + * Debrief: PC specific parts moved to separate source code file. 2.126 + * Debrief uses a lower-level function to format ISO 8601 date 2.127 + and time so as not to bring in the floating-point library's 2.128 + space overhead on platforms without an FPU. 2.129 + * Debrief plays a sound when turning the page. 2.130 + * PC: Delay for holding Esc to quit no longer depends on 2.131 + display refresh rate. Instead, it uses the global 60 Hz 2.132 + timer (reported by Sara). 2.133 + * PC: Draws speedometer even when preview is at right, 2.134 + as long as there is room, that is, 3 or fewer next pieces 2.135 + (requested by Cubicz). 2.136 + * PC: Gimmick choice moved to Options to match GBA/DS. 2.137 + * PC: Select scenario before starting game (bug 0041). 2.138 + * PC options uses standardized GBA names (OPTIONS_*) instead of 2.139 + PC names (LJPREFS_*). 2.140 + * PC options Shift sound scale, Side, and Next position moved to 2.141 + skin. 2.142 + * PC options sets initial options using initOptions() from GBA/DS. 2.143 + Incidentally, this restores the initial randomizer to bag 2.144 + (0.41 regression and may make similar regressions less likely. 2.145 + * PC options uses unpackOptions() from GBA/DS. 2.146 + * PC options sound is also routed through the cross-platform 2.147 + sound manager. 2.148 + * PC: lj.ini uses fourCCs as names for easier saving and loading. 2.149 + * PC: Switching between windowed and full screen in Options 2.150 + no longer needs the user to restart the program. 2.151 + * PC: LJVorbis doesn't crash if polled while the music is stopped. 2.152 + This can happen when cross-platform code plays sound effects 2.153 + during debrief. 2.154 + * PC: Skin controls window size, or full screen resolution 2.155 + (bug 0017). 2.156 + * PC/DS: Displays a description of some option values in addition 2.157 + to descriptions of options themselves. 2.158 + * GBA/DS: Options disabled on the PC are disabled here. 2.159 + * GBA: Menus are faster now that part of the text engine has been 2.160 + moved to fast RAM. 2.161 + * DS: Plays sound for game over (bug 0064). 2.162 + * DS: Options displayed on touch screen for future touch operation. 2.163 + * DS: Displays debrief after game ends (bug 0031). 2.164 + 2.165 +_____________________________________________________________________ 2.166 +0.41 (2007-12-18) 2.167 + * Piece set and randomizer are decoupled (requested by Lardarse 2.168 + and Dood77). 2.169 + * New piece set: iCheat(tm) deals only I tetrominoes, like an 2.170 + infamous cheat code for some other game (requested by DIGITAL). 2.171 + * New garbage style "Preload zigzag", which preloads a zigzag 2.172 + pattern of holes. 2.173 + * At debrief time, if at least two rows of the zigzag pattern 2.174 + or at least four rows of a rectum (1-block-wide opening) are 2.175 + formed, shows a secret grade based on the number of pattern 2.176 + rows instead of the number of blocks left in the playfield. 2.177 + * Debrief options section sorted by position in options menu. 2.178 + * Debrief allows for multiple pages of reports. 2.179 + 2.180 +_____________________________________________________________________ 2.181 +0.40a (2007-11-30) 2.182 + * Does not attempt to create a ghost piece before the first 2.183 + falling piece. This may fix a crash on some systems if the 2.184 + first game played during a session has deep drop turned on 2.185 + (reported by Rich Nagel on Windows 98 Second Edition). 2.186 + * Deep drop ghost piece code stops checking at the ceiling. 2.187 + 2.188 +_____________________________________________________________________ 2.189 +0.40 (2007-11-27) 2.190 + * Split out speed curves and randomizers to separate files called 2.191 + speed.c and random.c. 2.192 + * Adjusted master delays to be closer to TAP; 600-999 no longer 2.193 + overlap Death 000-399. 2.194 + * Displays short description of each option (requested by AXYPB). 2.195 + * Drill gimmick is back, with the new goal of clearing the bottom 2.196 + row (requested by DIGITAL). 2.197 + * Begin to add code for explosive line clear gimmick; not yet 2.198 + integrated (requested by Joshua). 2.199 + * Debrief shows number of floor kicks. 2.200 + * Bottom blocks is called Deep drop to match recent Multiblocks. 2.201 + * PC: Close box works in Options and Game Keys. 2.202 + * PC: Does not redraw title screen if close box is clicked. 2.203 + * PC: If skin sets transparentPF=1, hotlines and line clear 2.204 + animations are drawn transparent (requested by Rich Nagel). 2.205 + * GBA/DS: Can quit game from pause screen (requested by AXYPB). 2.206 + * GBA/DS: Copyright notice and options screen drawn with 2.207 + proportional font for readability. 2.208 + * GBA/DS: Menu uses the same sound manager that the game play 2.209 + uses, not the platform-specific sound manager. 2.210 + * GBA: Sound for end of game (requested by Lardarse). 2.211 + * DS: Closing the lid pauses the game and turns off the screens 2.212 + (requested by AXYPB). 2.213 + * DS: Unavailable hold piece is really grayed out. 2.214 + * DS: Real music and sound effects engine (requested by 2.215 + bob_fossil). 2.216 + 2.217 +_____________________________________________________________________ 2.218 +0.39 (2007-10-24) 2.219 + * Does not double line clear delay (unknown regression). 2.220 + * Source code includes new files options.h and old_pc_options.c 2.221 + which were mistakenly left out of 0.38 (reported by tr3). 2.222 + * Draws "hotline" symbol on lines being cleared (requested by 2.223 + Rosti LFC and kiwibonga). 2.224 + * Debrief no longer counts a zero line T-spin as a home run 2.225 + (0.28 regression; reported by Lardarse). 2.226 + * New randomizer 7+1-piece Bag adds one randomly selected 2.227 + tetromino to each bag (requested by colour_thief). 2.228 + * Refactored speed curve state information for pseudo-OO 2.229 + refactoring. 2.230 + * New option: Bottom blocks allows the falling piece to fall past 2.231 + blocks into an appropriately shaped hole (requested by mar). 2.232 + * Added bottom blocks and well size to debrief. 2.233 + * New gluing options Sticky and Sticky by color. 2.234 + * PC: Skin specifies the loop point and whether the music starts 2.235 + before or after the "Ready, Go" animation. 2.236 + * PC: Skin specifies music volume (requested by DIGITAL). 2.237 + * PC: Fixed off-by-one in sideways delay (reported by Lardarse). 2.238 + * PC: Fields with blkH > 24, especially blkH == 48 and Well height 2.239 + 10, are more likely to fit on screen (reported by Needle). 2.240 + 2.241 +_____________________________________________________________________ 2.242 +0.38 (2007-09-11) 2.243 + * Plays a .ljm specified at the command line (requested by 2.244 + Rich Nagel). 2.245 + * More refactoring for multiplayer. 2.246 + * Added wall kick opcode SKIP_IF3, similar to SKIP_IF except it 2.247 + also checks the two cells above the specified cell. 2.248 + * L, J, and T in Arika use SKIP_IF3 to check the entire center 2.249 + column when rotating to vertical (requested by edo). 2.250 + * Moves new tetrominoes to be between the walls (fixing Tengen in 2.251 + width 4) and, if "Enter above playfield" is off, down far enough 2.252 + so that they're within the playfield (requested by Caithness). 2.253 + * New rotation system: Climbing, based on Tetris DX. (No, you 2.254 + can't wall-climb yet because the sub-block behavior differs.) 2.255 + * Option "4x4 squares" is called "Gluing". Other gluing options 2.256 + are planned. 2.257 + * In Square gluing, formation of a square adds a delay 2.258 + equal to line clear delay. 2.259 + * PC: Refactored to share some of the GBA/DS options logic. 2.260 + * PC: Moved most sound effect code into pcsound.c. 2.261 + * PC: Sound for 4x4 square formation (cribbed from TOD). 2.262 + * PC: Reloads the default skin's configuration before loading 2.263 + the skin, so that skins always inherit from the default skin 2.264 + and not the last chosen skin (requested by Rich Nagel). 2.265 + * GBA: Uses duration as priority for square wave sound effects, 2.266 + which should prevent the section up sound from getting 2.267 + interrupted as often. 2.268 + * GBA/DS: Draws real-time speedometer. 2.269 + 2.270 +_____________________________________________________________________ 2.271 +0.37 (2007-07-09) 2.272 + * Requires "Up" and "Alt firm drop" to be pressed again when 2.273 + lock delay is turned off or less than DAS delay 2.274 + (requested by reivilo). 2.275 + * Keypress count is reset to 0 even in "Play .ljm" (reported by 2.276 + jujube). 2.277 + * Playing a broken .ljm from the gimmick menu gives an informative 2.278 + error message instead of crashing (0.32a regression; reported by 2.279 + Rosti LFC). 2.280 + * Rhythm section-up logic waits for 64 beats at L bpm, not L+1 bpm. 2.281 + * Fixed a potential buffer overflow in shuffleColumns (the "banana" 2.282 + item), brought on by the expansion of the playfield to 12 columns 2.283 + (0.33 regression). 2.284 + * Better comments in source code about wall kick entry macros 2.285 + WK(x, y), WKX(wk), WKY(wk) in lj.h (requested by Lardarse). 2.286 + * PC: Reinstate Bach music while investing a trademark claim. 2.287 + * PC: Separate music for Rhythm speed curves. 2.288 + * PC: Does not use stretch blitter when drawing the blocks of 2.289 + sprite pieces at full size (e.g. falling piece). 2.290 + * PC: blitField() blits adjacent rows in one call, which is 2.291 + faster on slow machines (requested by Matthew). 2.292 + * PC: Size of hold piece respects blkW and blkH. 2.293 + * README describes build instructions for GBA and DS. 2.294 + * For user convenience, the Allegro DLL is included with the 2.295 + executable, and the source code is in a separate archive. 2.296 + 2.297 +_____________________________________________________________________ 2.298 +0.36 (2007-05-21) 2.299 + * Allows DAS delay up to 400 ms (requested by Rich Nagel). 2.300 + * Option for entry within or above ceiling (requested by DIGITAL 2.301 + and Cubicz). 2.302 + * Option for hold piece behavior (requested by Lardarse). 2.303 + * PC: User can change individual game keys, like in StepMania 2.304 + (requested by DIGITAL and Rosti LFC). 2.305 + * PC: Minor changes to included background music. 2.306 + * PC: Option for playing a scale during sideways motion 2.307 + (requested by herc). 2.308 + * PC: Selecting a skin saves options (reported by Rich Nagel). 2.309 + * PC: When loading saved options, treats 16 ms DAS delay as valid 2.310 + (reported by jujube). 2.311 + * PC: Skin setting for a transparent background (requested by 2.312 + cdsboy and Bloodstar). 2.313 + * PC: Demo recording uses "Rec" and "Play" in addition to icons. 2.314 + * PC: Reallocates back buffer after each skin switch, avoiding 2.315 + problems related to changes in blkW and blkH (reported by 2.316 + Rich Nagel). 2.317 + * GBA/DS: Fixed line clear options (0.34 regression). 2.318 + * GBA/DS: Press Start after lose, rather than timing out to options 2.319 + (requested by Webby). 2.320 + * "Default" means not to pay back a loan. Manual uses "initial" 2.321 + or "preset" instead (requested by Bruce Tognazzini). 2.322 + 2.323 +_____________________________________________________________________ 2.324 +0.35 (2007-04-25) 2.325 + * New speed curve Game Boy Heart. 2.326 + * New speed curve Death 300+, which starts three sections into 2.327 + Death just like cgwg's cheat for TAP (requested by Amnesia). 2.328 + * New randomizer 10-piece Memoryless. 2.329 + * First garbage line takes into account well width (reported by 2.330 + Lardarse). 2.331 + * Displays section number for NES, Game Boy, and Game Boy Heart 2.332 + speed curves (requested by Rich Nagel). 2.333 + * Debrief displays Quadra-style pieces per active minute 2.334 + (requested by Cubicz). 2.335 + * Debrief calls pieces "pieces" instead of "tetrominoes" in 2.336 + 10-piece randomizers because I2, I3, L3 are not tetrominoes. 2.337 + * PC: Gimmick and level are displayed in skin foreground color 2.338 + (missed in 0.30; reported by Rich Nagel). 2.339 + * PC: Skin file name suffix is now .skin not .ini, so that skin.ini 2.340 + cannot be confused with lj.ini. The default skin is called 2.341 + default.skin not skin.ini. 2.342 + * PC: Title screen has option to select skin (requested by 2.343 + cdsboy). 2.344 + * PC: Block size is no longer hard-coded at 24x24 pixels. 2.345 + The new .skin commands blkW= and blkH= control their size 2.346 + (requested by Bloodstar). 2.347 + * PC: Source code comes with new installation guide written by 2.348 + Lardarse in "docs/Compiling_on_Windows.txt". 2.349 + 2.350 +_____________________________________________________________________ 2.351 +0.34a (2007-03-27) 2.352 + * PC: Fixed hidden level in Next at right mode (0.33 regression). 2.353 + * DS: Fixed hidden sprites due to bug in devkitARM R20 libnds 2.354 + (0.34 regression). 2.355 + * DS: New touch screen code should eliminate erroneous low notes 2.356 + played upon pen-up. 2.357 + 2.358 +_____________________________________________________________________ 2.359 +0.34 (2007-03-23) 2.360 + * Fixed Move to Back piece set (0.14 regression: dang!). 2.361 + * Separate options for number of previewed pieces above shadow and 2.362 + outside field (requested by kiwibonga, caffeine, and Cubicz). 2.363 + * Option to disable IRS (requested by DIGITAL). 2.364 + * Garbage gimmicks (vs., drill 40, hr derby) replaced with new 2.365 + garbage option (requested by Cubicz). 2.366 + * In garbage style "Vs.", player can choose difficulty 1 through 4 2.367 + (requested by Caithness). 2.368 + * Option for line clear delay, independent of entry delay 2.369 + (requested by kiwibonga). 2.370 + * Garbage style Drill no longer automatically tops out on 2.371 + short playfields. 2.372 + * Garbage style Vs. generates garbage with SZSZ randomizer. 2.373 + * Frequency of Vs. w/Items bananas is independent of randomizer. 2.374 + * Fixed self-clearing garbage (0.33 regression). 2.375 + * GBA/DS: Fixed pause button hiding left and right walls 2.376 + (0.33 regression). 2.377 + * GBA/DS: R button also works as hold piece (requested by 2.378 + PetitPrince). 2.379 + 2.380 +_____________________________________________________________________ 2.381 +0.33 (2007-03-08) 2.382 + * New speed curve Rhythm Zero is identical to Rhythm except it 2.383 + operates at 0G instead of 20G (requested by Cubicz and caffeine). 2.384 + * Speed curves that do not use level reset level to 0. 2.385 + Therefore, playing a game of Death then a game of Exponential 2.386 + on GBA/DS no longer shows the level that the player finished 2.387 + Death on as the level during Exponential. 2.388 + * Maximum well width increased to 12 (requested by Zed0). 2.389 + * Hold piece does not reset floor kicks, lock delay time in entry 2.390 + reset, or Rhythm's placement timer. 2.391 + * GBA/DS: More shared code moved to file "ljgbads.inc". 2.392 + * PC: Stops end-of-section sound when game is over 2.393 + (requested by Bloodstar). 2.394 + 2.395 +_____________________________________________________________________ 2.396 +0.32a (2007-02-25) 2.397 + * PC: Fixed demo recording (0.32 regression; reported by Rosti LFC) 2.398 + * PC: Option to record all games from the start 2.399 + (requested by cdsboy, DIGITAL, Cubicz, and colour_thief). 2.400 + * PC: Added Play .ljm gimmick: 2.401 + Allegro file selector pops up, and the player can choose the 2.402 + filename of a demo to play back (requested by Lardarse). 2.403 + 2.404 +_____________________________________________________________________ 2.405 +0.32 (2007-02-13) 2.406 + * Changed a CPU yield behavior that was affecting battery 2.407 + efficiency on the GBA and DS front ends (reported by Mighty Max). 2.408 + * Removed Low Rider gimmick in favor of option for well height 2.409 + (requested by Matthew). 2.410 + * Option for well width, like Shimizu's Tetris Semipro-68k. 2.411 + Combine this with a low well height to simulate BIG mode of 2.412 + TGM and Heboris (requested by Matthew). 2.413 + * Options for entry delay and sideways delay have "max" added to 2.414 + their description to clarify things (requested by kotetsu213 and 2.415 + Ezzelin). 2.416 + * All speed options given in Hz or G are given in both. 2.417 + * PC: Next above shadow works in all playfield positions (reported 2.418 + by caffeine). 2.419 + * GBA/DS: Added option for soft drop speed (requested by Ezzelin). 2.420 + * GBA/DS: Some shared code moved to file "ljgbads.inc". 2.421 + * GBA: Unavailable hold piece is grayed out. 2.422 + * GBA/DS: Lock delay = no lock works (reported by Ezzelin). 2.423 + * GBA/DS: Fixed frames/ms display of delays (reported by Ezzelin). 2.424 + 2.425 +_____________________________________________________________________ 2.426 +0.31 (2007-02-03) 2.427 + * Draws the score and next pieces before blitting the playfield 2.428 + in case a front-end draws the score or next pieces inside the 2.429 + playfield. 2.430 + * In Baboo!, speed curve Zero sets the level to the number of 2.431 + keypresses so far. 2.432 + * PC, GBA: Plays sound effect for end of section in speed curves 2.433 + that use sections (Master, Death, NES, Game Boy). 2.434 + * PC: Option to draw next pieces inside the playfield, above the 2.435 + shadow (requested by caffeine, cdsboy, and Cubicz). 2.436 + * GBA, DS: Blocks within a piece are drawn connected once they lock 2.437 + (TOD parity, requested by Lardarse and Bloodstar). 2.438 + * GBA, DS: Baboo! no longer double-counts console buttons passed 2.439 + through the joypad reading code. 2.440 + * README: "color=image" clone-and-hack error fixed (reported by 2.441 + Bloodstar). 2.442 + * README: Explains black rectangle in upper left corner as an 2.443 + icon for stop (requested by herc). 2.444 + * README: Explains controls on GBA and DS. 2.445 + 2.446 +_____________________________________________________________________ 2.447 +0.30 (2007-02-01) 2.448 + * Fixed ARE display in debrief, which could cause a crash in some 2.449 + situations (0.26 regression?). 2.450 + * Garbage no longer self-clears in Cascade gravity (reported by 2.451 + Bloodstar). 2.452 + * In high gravity, land a T with one block over an overhang and 2.453 + rotate it once so that it falls and clears a line. This is no 2.454 + longer counted as a T-spin. 2.455 + * During a replay, counts keypresses by the replay player. 2.456 + * PC: Scales undersized background images to the size of the window 2.457 + (requested by Bloodstar). 2.458 + * PC: If a piece enters during a skipped frame, a second upward 2.459 + trail is no longer drawn (reported by Bloodstar and caffeine). 2.460 + * PC: "Next at top" layout draws speedometer (requested by 2.461 + matt_hatter83). 2.462 + * PC: Trails option is saved properly (reported by caffeine). 2.463 + * PC: User can customize the game's text and background colors 2.464 + using skin.ini (requested by Bloodstar). 2.465 + * Added rudimentary port to Nintendo DS for people with MAX Media 2.466 + Dock, M3 Pro, SuperCard Rumble, or SLOT-1 cards, which can run 2.467 + DS homebrew but not GBA homebrew. 2.468 + * Moscow Nights removed from lj-contrib due to research into the 2.469 + copyright term extensions enacted by Russian Federation during 2.470 + the 1990s. 2.471 + 2.472 +_____________________________________________________________________ 2.473 +0.29 (2007-01-17) 2.474 + * TDS scoring recognizes chains properly (reported by Lardarse). 2.475 + * TDS scoring section factor stops increasing after 190 lines. 2.476 + * Added NES scoring method. 2.477 + * Added scoring for soft and hard drops to options and debrief. 2.478 + * Cascade no longer deletes the bottom row when lines high on the 2.479 + playfield are cleared (reported by Lardarse). 2.480 + * PC: Draws trails when a tetromino goes up or down rapidly. 2.481 + (Option to turn them off.) 2.482 + * PC: Handles dirty rectangles for next pieces and score 2.483 + separately. 2.484 + * Scoring section of README describes all scoring methods. 2.485 + 2.486 +_____________________________________________________________________ 2.487 +0.28 (2007-01-04) 2.488 + * Added Cascade gravity, as seen in Quadra, Tetris Worlds Cascade, 2.489 + and Tetris DS Touch. 2.490 + * Master/Death: Level starts at -1, so that the first piece is 2.491 + played at level 0 (requested by Lardarse). 2.492 + * Changed T-spin detection to save whether or not a rotation 2.493 + involved a kick (0: move; 1: rotate; 2: rotate with kick) so 2.494 + that scoring methods such as TDS can score T-spins with and 2.495 + without kicks differently. 2.496 + * Added TDS scoring method (line clears only). 2.497 + * New, more general, possibly easier to understand reshuffle code 2.498 + in bag randomizer (requested by Lardarse) 2.499 + * GBA: Gold and silver square colors added (requested by Lardarse). 2.500 + * PC: Game over in low ceiling no longer fades the area outside the 2.501 + playfield (reported by Lardarse). 2.502 + * PC: Close box interrupts starting and game over animations. 2.503 + * README warns that 4x4 squares mode needs ljconn. 2.504 + 2.505 +_____________________________________________________________________ 2.506 +0.27 (2006-12-20) 2.507 + * GBA version includes a valid header (requested by Ezzelin). 2.508 + * Fixed incorrect spawn orientations for Game Boy rotation system 2.509 + (0.25? regression, reported by Lardarse). 2.510 + * Fixed incorrect win/loss sound early in 180 seconds (0.25 2.511 + regression). 2.512 + * New randomizer 10-piece Bag, including the domino and both 2.513 + trominoes. 2.514 + * GBA version now reports entry and lock delays in both frames 2.515 + and milliseconds. 2.516 + * Possibly fixed off-by-one in Game Boy speed curve section 2.517 + computation, which caused an incorrect slowdown in 210-219 2.518 + section (reported by Ezzelin). 2.519 + 2.520 +_____________________________________________________________________ 2.521 +0.26 (2006-12-15) 2.522 + * Option to allow game to proceed in the background (requested by 2.523 + Ezzelin). However, Windows doesn't appear to pass joystick 2.524 + presses to the game running in the background. FCE Ultra has 2.525 + what appears to be exactly the same problem. 2.526 + * Added Game Boy and NES speed curves. 2.527 + * Options and Game Keys are accessed through title screen, not 2.528 + gimmick selection. 2.529 + * Option to hide playfield without changing the skin. 2.530 + * Rearranged rules section of options to correspond more closely to 2.531 + the sequence of operations for each tetromino. 2.532 + * Soft or hard drop set to "lock on release" no longer produces 2.533 + double locks at 20G. 2.534 + * Disable initial hard drop when set to "lock" and entry delay is 2.535 + greater than 0 but less than DAS delay. 2.536 + * Checks for 4x4 squares top to bottom per comparison with The New 2.537 + Tetris (N64). 2.538 + * Finer grained selections in entry delay (requested by 2.539 + matt_hatter83) and lock delay (requested by caffeine). 2.540 + They now are at 50ms increments at the low end. 2.541 + * Expanded TGM speed curve to 12 sections. One comparatively slow 2.542 + section at 20G was added to Master before Death-equivalent starts 2.543 + (now Death 0 is equivalent to Master 600, not 500), and one 2.544 + faster section was added to the end of Death based on info posted 2.545 + to wiki by colour_thief. 2.546 + * Added H.R. Derby gimmick: 2.547 + Like Marathon, but every line you clear other than with a 2.548 + home run or a T-spin gives you garbage. 2.549 + * Ready Go animation is 1.2 seconds, not 2.0 seconds. 2.550 + * Preliminary support for Game Boy Advance, with experimental 2.551 + paged options replacing scrolling options. 2.552 + 2.553 +_____________________________________________________________________ 2.554 +0.25 (2006-11-28) 2.555 + * Fixed History 6 Rolls deciding between the first piece algorithm 2.556 + and the subsequent pieces algorithm (reported by Lardarse). 2.557 + * Fixed a signedness issue that broke 180 seconds gimmick in the 2.558 + case that a tetromino was kept active between 3:00 and 3:01 (0.24 2.559 + regression; reported by caffeine). 2.560 + * Allows ending the game with a piece in mid-air (0.24 regression). 2.561 + * Removed Arika and renamed Arika+TI to Arika. For the old 2.562 + Arika behavior, set Options:Floor kicks to 0. 2.563 + * Options screen allows DAS, allowing future versions to make more 2.564 + options and more values for each option available in a less 2.565 + cumbersome way. 2.566 + 2.567 +_____________________________________________________________________ 2.568 +0.24 (2006-11-16) 2.569 + * Added Score style to options and debrief. 2.570 + * Choice of LJ or TNT64 scoring is based on chosen score style, not 2.571 + whether 4x4 squares are turned on. 2.572 + * Added Hotline scoring method, where only lines cleared on 2.573 + specific rows count toward scoring. When enabled, draws white 2.574 + lines through empty spaces in these rows. 2.575 + * New soft drop and hard drop lock setting: Lock on release. It's 2.576 + a spring-loaded system: When you press the button, it doesn't 2.577 + lock, but when you let go, it locks (requested by colour_thief). 2.578 + * Option to override lock delay time (requested by caffeine). 2.579 + * Option to limit upward kicks (requested by Needle). This may 2.580 + obviate the difference between Arika and Arika+TI. 2.581 + * Sped up line clear in Master 300-499 to make less of a jarring 2.582 + transition to "death" style timings. 2.583 + * LJM loading fails even more gracefully on wrong format version, 2.584 + properly treating an LJM of the wrong version as not existing. 2.585 + * Fixed bug in 0.23's application switch pausing where switching 2.586 + away while already paused would require two Esc presses to 2.587 + continue (reported by caffeine). 2.588 + * Does not stop play until after the line clear animation finishes. 2.589 + This allows the game to properly update the single, double, 2.590 + triple, etc. counts for the last line that completes the goal 2.591 + (0.19 scoring regression?; reported by caffeine). 2.592 + * Refactored duplicated option loading code. 2.593 + * Options: Ditched parallel struct and array in favor of a 2.594 + single list of named indices. 2.595 + * Corrected Moscow Nights and Kalinka in lj-contrib for greater 2.596 + compatibility with obscure s3m players that can't handle odd 2.597 + numbers of channels (requested by Lardarse). 2.598 + 2.599 +_____________________________________________________________________ 2.600 +0.23 (2006-11-04) 2.601 + * Fixed S and Z in Sega 1988. 2.602 + * DUMB has been wrapped in a library called LJMusic and can be 2.603 + turned off at compile time (edit ljmusic.c and makefile) if you 2.604 + don't want to install DUMB. 2.605 + * Added support for Xiph.org's OggVorbis codec to LJMusic 2.606 + (requested by Needle). 2.607 + * Option to ignore sideways movement on first frame (like TGM 2.608 + series) (requested by Needle, seconded by caffeine). 2.609 + * Properly pauses the game when the player switches away from the 2.610 + window (requested by Lardarse). 2.611 + * Fails gracefully (does nothing) instead of crashing when loading 2.612 + replay files of a different format version. 2.613 + * Moved as much as possible below setting display mode so that 2.614 + people porting LJ to other platforms can toss up working alert() 2.615 + boxes earlier (requested by cdsboy). 2.616 + 2.617 +_____________________________________________________________________ 2.618 +0.22 (2006-10-23) 2.619 + * Fixed color system change after reloading (0.20 regression?, 2.620 + reported by lardarse). Should always unpack options just before 2.621 + loading skin. 2.622 + * All numbers in saved states are stored big-endian, making movies 2.623 + compatible between 0.22 for Windows and Intel Mac and 0.22 for 2.624 + PowerPC Mac (requested by cdsboy). 2.625 + * Sega rotation systems renamed to Arika for less confusion with 2.626 + the rotation systems in Sega's Tetris games from 1988 and 1998 2.627 + (requested by Needle). 2.628 + * Added Sega 1988 rotation system (like Arika without wall kicks). 2.629 + * Preliminary Vorbis playback code is included in the source code 2.630 + distribution but has not been activated in the program. 2.631 + (The keys to compiling OggVorbis on MinGW+MSYS are 1. set the 2.632 + prefix to match the MINGDIR you used to build Allegro, and 2.633 + 2. disable creation of the shared library.) 2.634 + 2.635 +_____________________________________________________________________ 2.636 +0.21 (2006-10-21) 2.637 + * Added new T-spin detection rule "3-corner T no kick", identical 2.638 + to 3-corner T except that a wallkick is not counted as a rotation 2.639 + (requested by kotetsu213). 2.640 + * Options that don't apply because of how another option is set 2.641 + are grayed out with an explanation (requested by Needle). 2.642 + * Debrief tells number of blocks left in the playfield and whether 2.643 + saved state was used (requested by caffeine). 2.644 + * Debrief tells what level the player stopped on. 2.645 + * Added support for saving input stream ("demo" or "movie") to file 2.646 + "demo.ljm". Press [ to start/stop recording and ] to start/stop 2.647 + playback (requested by caffeine). 2.648 + * Pieces are actually random again (0.20 regression). 2.649 + * New lockdown mode: Entry reset. Instead of resetting, the lock 2.650 + timer pauses while the piece is falling (requested by caffeine). 2.651 + * No +20 for banking more than 5 beats (requested by caffeine). 2.652 + * 250 ms entry delay option (requested by matt_hatter83). 2.653 + * Entry and sideways delays in Master and Death use the shorter of 2.654 + the speed curve's delay and the user's delay. 2.655 + * Added soft drop speed to options. 2.656 + * Added settings 8.6 Hz through 6.7 Hz to Sideways speed. 2.657 + 2.658 +_____________________________________________________________________ 2.659 +0.20 (2006-10-15) 2.660 + * Fixed color system change after options (0.19 regression). 2.661 + * Restored ability to hold first piece (0.17 regression?). 2.662 + * Requires down to be re-pressed only when entry delay is less 2.663 + than sideways delay. 2.664 + * In modes with ARE, allow initial hard drop even on first piece 2.665 + (requested by caffeine). 2.666 + * Visual refresh in Game Keys. 2.667 + * Game Keys ignores keypresses for 1/4 second after each key is 2.668 + set, which should fix problems with GameCube to USB adapters 2.669 + (requested by Caithness). 2.670 + * All platforms use a single randomizer, whose state is associated 2.671 + with the playfield. 2.672 + * ARE is a binary option again, as several speed curves ignored the 2.673 + old version's difference between constant and decreasing. 2.674 + * Moved version display to title screen. 2.675 + * Sound effect for win differs from that for game over. 2.676 + * Sound effect for rotating a piece on its first active frame. 2.677 + * Begun to add assertions, causing game over if they fail. 2.678 + * Added support for saving the state of the playfield. 2.679 + Press [ to save or ] to load. Movies might be next. 2.680 + 2.681 +_____________________________________________________________________ 2.682 +0.19 (2006-09-28): 2.683 + * Use of SRS or Sega colors is determined by a flag set for each 2.684 + rotation system. The rule is that rotation systems that use 2.685 + bounding box rotation should use SRS colors. 2.686 + * Added TOD M4 rotation system (face up entry + bounding box 2.687 + rotation + roughly TGM style compensation). 2.688 + * List items in Options flicker much less. 2.689 + * Beginning of an actual title screen. 2.690 + * Removed Rhythm gimmick in favor of speed curve option. 2.691 + * Added Master and Death speed curves (requested by Needle). 2.692 + Death hasn't been tested thoroughly because the developer 2.693 + sucks at death. 2.694 + * Blits only those playfield rows that have changed, making 2.695 + animation smoother on old, cheap, or mobile video cards 2.696 + (requested by cosmonaut). 2.697 + * Rhythm speed curve no longer has what feels like a big elbow 2.698 + (requested by caffeine). Specifically it gives 20 points 2.699 + instead of banking more than 5 pieces' worth of time. 2.700 + * Doesn't draw the state after the tetromino spawns but before 2.701 + initial rotation has taken effect (requested by Needle). 2.702 + * Added sound effect for hold piece (requested by Needle). 2.703 + * Land and lock sound much better (requested by Needle). 2.704 + * Shuffle columns (seen only in Vs. w/ Items) correctly disconnects 2.705 + blocks horizontally. 2.706 + * In Square mode, T-spins cause avalanches. 2.707 + * In Square mode, lines containing a piece of a 4x4 block are worth 2.708 + more, and homers are worth less. 2.709 + * Fixed ignoring diagonal presses (0.17 regression). 2.710 + * Created a new struct "LJPrefs" to hold preferences set by the 2.711 + player in the Options menu separately from the parameters 2.712 + that the game actually uses. This allows gimmicks to override 2.713 + preferences more cleanly. 2.714 + * Added support for future scoring methods where the score per line 2.715 + at a given point in the chain is based on a formula. 2.716 + * Created a union that combines the new struct with the array that 2.717 + the Options menu edits to improve maintainability of Options. 2.718 + 2.719 +_____________________________________________________________________ 2.720 +0.18 (2006-09-16): 2.721 + * Fixed Classic lock reset (unknown regression). 2.722 + * Fixed misbehavior when hold is pressed after a piece 2.723 + lands but before it locks (0.17 regression). 2.724 + * Shadow is drawn using the piece's color, at 25% opacity, 2.725 + from rows 4 and 5 of ljblocks. 2.726 + * Fixed shadow option saving (0.17a regression). 2.727 + * Added option to hide shadow color or change shadow opacity. 2.728 + * Split Sega rotation system into one with and one without 2.729 + TGM3's upward kicks. 2.730 + * Added NES and Game Boy rotation systems. 2.731 + * README: Illustration of game play has an image map. The reader 2.732 + can select (hover over) a region of the screenshot, and the 2.733 + browser will show the title of the region as a tooltip. 2.734 + * BPM is now called speed level in preparation for other 2.735 + speed curves (including TGM and TA Death). 2.736 + 2.737 +_____________________________________________________________________ 2.738 +0.17 (2006-09-13): 2.739 + * Option for sticky gravity per color, as seen in The Next Tetris. 2.740 + * "Ready, Go" is centered, even in low ceiling gimmicks. 2.741 + * Added TGM3's upward kick to Sega T rotations. 2.742 + * Added Tengen rotation system. 2.743 + * Removed TGM gimmick in favor of entry delay option. 2.744 + * Initial hold works at any time. The key can be pressed and 2.745 + released during entry delay or even during line clear delay. 2.746 + * Initial actions do not inflate keypress count 2.747 + (helpful in Baboo! with entry delay). 2.748 + * Draws hold piece in garbage colors when it is not available. 2.749 + * Options scroll. 2.750 + * Option to draw next pieces to right in constant size or above. 2.751 + * Better looking scroll bar in options. 2.752 + * Debrief: In naive gravity, reports T-spin singles, doubles, 2.753 + and triples separately from non-T-spins. In other line 2.754 + clear gravity modes, "home run" is now called "quad". 2.755 + * Fixed a buffer overflow bug in sticky: fillCLoop() no longer 2.756 + treats the right side of one row and the left side of the 2.757 + row above it as one region. 2.758 + * Clarified manual as to the purpose of the Vs. gimmicks. 2.759 + * Separated platform specific stuff into a separate struct. 2.760 + * Refactored scoring into a separate function for future movement 2.761 + to gimmick and option control. 2.762 + * Debrief formats the report into a string and then writes it 2.763 + to a file and the screen all at once. 2.764 + * Refactored play() to move everything in the game loop that is 2.765 + not platform specific out of ljpc.c into new file ljplay.c. 2.766 + * Wall kick table is no longer flipped, making it easier to add 2.767 + new rotation systems. 2.768 + * Unified counterclockwise and clockwise rotation code. 2.769 + 2.770 +_____________________________________________________________________ 2.771 +0.16 (2006-09-03): 2.772 + * Fixed instant sideways speed (0.15 regression). 2.773 + * In countdown modes, debrief screen displays whether clear was 2.774 + successful. 2.775 + * 6-piece Bag no longer makes Vs. ridiculously easy. 2.776 + * Counts score and garbage separately for future shift to 2.777 + gimmick-controlled scoring. 2.778 + * Displays score instead of garbage on the play screen. 2.779 + * Counts singles, doubles, triples, and homers, and displays them 2.780 + in debrief. 2.781 + * Option to hide falling piece in addition to shadow. 2.782 + * Can load mod, xm, or it music in addition to s3m. 2.783 + * Moved skin description from lj.ini to skin.ini, and the name of 2.784 + this skin description file name can be changed with the Skin= 2.785 + command in lj.ini. 2.786 + * Closes lj.ini when reading it. 2.787 + * All vkey->action game key handling code moved to macro.c for 2.788 + future refactoring. 2.789 + * Build process uses automatic generation of C files' dependencies 2.790 + for future refactoring of header files. 2.791 + * Added two versions of NES-style blocks to lj-contrib: 2.792 + one by tepples and one by deepdorp. 2.793 + 2.794 +_____________________________________________________________________ 2.795 +0.15 (2006-08-27): 2.796 + * Options for DAS delay, number of next pieces, and window vs. 2.797 + full screen. 2.798 + * Options scrolling doesn't cause the dialog's title and 2.799 + instructions to flicker. 2.800 + * Small font's 5 glyph is the same width as other digits. 2.801 + * Fixed O shape table. 2.802 + * Macros (vkey->action mappings past the first 8) are in a 2.803 + lookup table in the new file macro.c. This paves the way 2.804 + for a future macro editor. 2.805 + * Added macros Alt. Firm Drop and Alt. Hold. 2.806 + * Game Keys displays names for all keys, not just printable keys. 2.807 + * In Sega rotation mode, reads block images from ljblocks-sega.bmp 2.808 + and ljconn-sega.bmp if available. 2.809 + * If ljbg.jpg is not available, uses a plain white backdrop. 2.810 + * Reads file names for block images, background image, and music 2.811 + from lj.ini, which the player can edit with a text editor. 2.812 + * New "6-piece Bag" randomizer is one stick short of a bundle. 2.813 + * Added an arrangement of the minuet from JS Bach's French Suite 2.814 + in B minor (BWV 814) to lj-contrib. 2.815 + 2.816 +_____________________________________________________________________ 2.817 +0.14 (2006-08-18): 2.818 + * pause() renamed to pauseGame() to fix namespace collision on 2.819 + platforms with unistd.h (Linux, BSD, Mac). 2.820 + * Gimmick settings for initial gravity, ARE, and ceiling height 2.821 + are initialized from a lookup table. 2.822 + * History 6 Rolls randomizer uses correct initial history (ZSZS) 2.823 + and correct selection for the first piece (I, J, L, or T). 2.824 + * Blocks are drawn connected within each tetromino if ljconn.bmp 2.825 + is present. 2.826 + * Corrected connection table for O tetromino. 2.827 + * Rhythm's BPM counter increases after every 64 beats, not every 2.828 + 64 tetrominoes. 2.829 + * Added Low Vs. gimmick: 2.830 + Like Vs. CPU with a low ceiling. 2.831 + 2.832 +_____________________________________________________________________ 2.833 +0.13 (2006-08-15): 2.834 + * Hard drop lock option "Zangi" renamed to "Slide". 2.835 + * Added soft drop lock option. 2.836 + * Ignores diagonal presses differently: instead of pretending the 2.837 + keys are up, it pretends that they haven't changed from the last 2.838 + frame. UP, UP+RIGHT, UP no longer makes a double hard drop. 2.839 + * Esc pauses the game and music instead of immediately quitting. 2.840 + * Added contributions from cdsboy to lj-contrib. 2.841 + 2.842 +_____________________________________________________________________ 2.843 +0.12 (2006-08-13): 2.844 + * Creates lj.ini if it doesn't exist, instead of crashing on 2.845 + startup. 2.846 + * Added half-size Aver16 font. 2.847 + * Zero gravity (as in Baboo!) + Zangi hard drop = land and lock, 2.848 + instead of just sit there 2.849 + * Option to use classic (Game Boy style) or step-reset (TGM style) 2.850 + lockdown instead of move-reset (SRS style) lockdown. 2.851 + * Contains connection tables for all pieces. Not used yet, 2.852 + but will be used for Cascade (where gravity is based on shapes 2.853 + of pieces), Square (which forms big squares only out of whole 2.854 + tetrominoes), and Bombliss (which doesn't place a bomb in a block 2.855 + that has blocks on opposite sides of it within the piece, such as 2.856 + the middle of an I, J, L, or T). 2.857 + * Pieces can carry bomb blocks within them. Not used yet, 2.858 + but will be used for Bombliss. 2.859 + * Option for sticky gravity. This will also be used for Bombliss. 2.860 + * Options rearranged: game on top, control in middle, 2.861 + view on bottom. 2.862 + * Options menu uses a smaller font and scrolls if necessary (but 2.863 + it's not necessary just yet). 2.864 + * Zero isn't wider than other digits in the large or small font. 2.865 + * Split debrief screen into debrief.c. 2.866 + * Debrief displays lockdown and line clear gravity type. 2.867 + * List of options in debrief is written with smaller font. 2.868 + * Config screen does a better job of ignoring simultaneous presses 2.869 + on USB joystick adapters that map the Control Pad to both axes 2.870 + and buttons (such as EMS USB2). 2.871 + * Main menu and options always respond to the arrow keys (in 2.872 + addition to the keys set in Game Keys), allowing players to 2.873 + reset Game Keys even when the keys have become corrupted. 2.874 + 2.875 +_____________________________________________________________________ 2.876 +0.11 (2006-08-01): 2.877 + * Added Instant, 20 Hz, 15 Hz, and 12 Hz sideways speeds. 2.878 + * Added randomizer History 6 Rolls, very similar to Move to Back 2.879 + but sometimes repeating recent tetrominoes. 2.880 + * Allows separate wall kick tables for clockwise and anticlockwise 2.881 + rotation. 2.882 + * New "theta=default" in piece->block expansion reflects the 2.883 + rotation system's initial orientations for new pieces, next 2.884 + pieces, and the hold piece. 2.885 + * Option to use Sega rotations instead of SRS rotations. 2.886 + * Option for ignoring diagonal presses. 2.887 + * Option for locking or not when using hard drop (Up). 2.888 + * Option to turn off smooth gravity animation. 2.889 + * Option to use TNT style T-spin detection instead of TDS style. 2.890 + * Debrief displays rotation system and T-spin detection. 2.891 + * Saves options to lj.ini. 2.892 + * Split options screen and wall kick table into separate source 2.893 + code files. 2.894 + * Plays music in stereo. 2.895 + * Site distributes contributed graphics and music files to 2.896 + replace the defaults. 2.897 + 2.898 +_____________________________________________________________________ 2.899 +0.10 (2006-07-27): 2.900 + * Set default full screen color depth to 16-bit which may be more 2.901 + compatible than 15-bit. 2.902 + * Press Print Screen to save the current screen as ljsnap.bmp. 2.903 + * Plays next piece sound whenever the next column moves up. 2.904 + 0.08-0.09 didn't do so for the first hold in a game. 2.905 + * No longer tries rotating right when rotating left fails. 2.906 + * Handles DirectDraw amnesia (screen corruption in Alt+Tab) 2.907 + correctly in menu, game play, and debrief screens. 2.908 + * Asset and video buffer cleanup consolidated into one function. 2.909 + * Gravity speeds up 50% faster. 2.910 + * Added options menu for selecting sideways movement speed and 2.911 + randomizer. 2.912 + * Added more randomizers: 14-piece bag, move-to-back (TGM style), 2.913 + memoryless (classic), and SZSZ (used in the well-known proof 2.914 + that memoryless cannot be played indefinitely). 2.915 + * Debrief screen displays date and time of report generation as 2.916 + well as randomizer and sideways speed, and logs what it displays 2.917 + to lj-scores.txt. 2.918 + * Moved all gimmick-specific code from lj.c to new file gimmicks.c. 2.919 + * Built against Allegro 4.2.0. (Users of previous versions will 2.920 + need the new DLL.) 2.921 + * Countdown based gimmicks (40 lines, 180 seconds, Baboo!, 2.922 + TGM World, Drill 40) use the same countdown variable for 2.923 + end state detection that they had used for countdown sound. 2.924 + * Support for tracked music using DUMB. 2.925 + * Manual is HTML. 2.926 + 2.927 +_____________________________________________________________________ 2.928 +0.09 (2006-07-16): 2.929 + * Reverted sideways kicks of I tetromino rotating from horizontal 2.930 + to vertical based on testing I-I interactions in Tetris DS. 2.931 + * Key pretending is more consistent. 2.932 + * TGM World stops at 290 lines, more like Tetris The Grand Master. 2.933 + * In gimmicks with ARE (currently TGM World), allows initial 2.934 + hard drop. 2.935 + * Licensed as free software under GNU General Public License. 2.936 + * Fixed incomplete first bag (0.08 regression). 2.937 + * Has its own icon instead of the wall kick editor's icon. 2.938 + * No double initial rotation when using initial hold in TGM World. 2.939 + * Input actions are a 32-bit record for future recording. 2.940 + * Hidden next piece is a separate variable, which next piece 2.941 + sound also respects, fixing Items. 2.942 + 2.943 +_____________________________________________________________________ 2.944 +0.08 (2006-07-08): 2.945 + * Plays sounds for the next piece like TGM. 2.946 + * No longer allows 800x552 window now that 0.08 displays the 2.947 + gimmick name. 2.948 + * Supports spawn delay and line delay. 2.949 + * Supports initial hold and rotation. 2.950 + * Supports variable garbage randomness. 2.951 + * Begun to move gimmick code out of core. 2.952 + * Added TGM World gimmick: 2.953 + Standard S.M.G., except there's a half-second delay before each 2.954 + tetromino, and a half-second delay after each tetromino that 2.955 + forms at least one line. So if you want fast play, make more 2.956 + lines at once. 2.957 + * Added Drill 40 gimmick: 2.958 + Like 40 lines, but the screen starts with 18 rows of garbage. 2.959 + Be prepared to rely on SRS infinite spin for the first few 2.960 + lines until your skill improves. 2.961 + 2.962 +_____________________________________________________________________ 2.963 +0.07 (2006-06-30): 2.964 + * Added Baboo gimmick: 2.965 + Standard S.M.G., except there is no gravity, and the game 2.966 + ends after 300 keypresses. 2.967 + * Cleaned up the font a bit. 2.968 + * Corrected wall kicks for I tetromino to match an updated 2.969 + description provided by nicholas. Specifically, I tetromino 2.970 + prefers kick-down to kick-up. 2.971 + * Removed sound-not-found debug message. 2.972 + * Plays "ready, go!" like TGM. 2.973 + * Bigger font for lines and time. 2.974 + 2.975 +_____________________________________________________________________ 2.976 +0.06 (2006-06-25): 2.977 + * Gimmicks are sorted into columns. 2.978 + * Debrief responds to controller mapping. 2.979 + * Debrief shows keypresses per tetromino and garbage per minute. 2.980 + * Loads/saves vkey configuration from/to disk instead of 2.981 + prompting the user every time the program starts. 2.982 + * Build system switched to GNU Make. 2.983 + * Terminal window ("DOS box") hidden. 2.984 + * Responds to window system's close button. 2.985 + * Counts down 5-4-3-2-1 in line or time limited gimmicks. 2.986 + * Icon in the top left corner of the window is customized. 2.987 + * Displays ljbg.jpg in the window's blank space. 2.988 + * Displays blocks from ljblocks.bmp. 2.989 + * Releases the CPU for 5ms per frame when possible, so that 2.990 + background tasks such as a music player can keep up. 2.991 + * Increased sound effects' volume so that music players do not 2.992 + completely overpower them. 2.993 + * Added macros for rotate twice, move far left, and move far right. 2.994 + * Corrected fix to alternation between falling and landed states 2.995 + that occurred especially when sliding a tetromino to the right 2.996 + under high gravity. 2.997 + * Added Items gimmick: 2.998 + Standard S.M.G., except after the first 7 pieces you get 2.999 + random starting orientations, no rotation, and hidden next 2.1000 + pieces, and the speed goes to 1G. Every time you are given an 2.1001 + I piece, either you get 2 lines of garbage or the columns of 2.1002 + blocks in the well are shuffled. 2.1003 + * Fixed scoring tables in README.txt. 2.1004 + 2.1005 +_____________________________________________________________________ 2.1006 +0.05 (2006-06-23): 2.1007 + * Included readme.txt. 2.1008 + * Debrief report shows more decimal places in time and PPM. 2.1009 + * Game over plays a simple animation and sound. 2.1010 + * Playfield can be resized (with a recompile). 2.1011 + * Removed hardcoded key binding help text. 2.1012 + * Window is bigger in preparation for wallpaper. 2.1013 + * Display mode is 15/16/24/32-bit in preparation for wallpaper. 2.1014 + * Added Low Rider gimmick: 2.1015 + Standard S.M.G. with an 8-block-tall visible playfield. 2.1016 + 2.1017 +_____________________________________________________________________ 2.1018 +0.04 (2006-06-22): 2.1019 + * Uses an OS-native bitmap rather than an Allegro bitmap, 2.1020 + which may allow faster drawing with some video cards. 2.1021 + * Allows play from a keyboard or a joystick. 2.1022 + * Presents a key configuration screen when the program starts. 2.1023 + * A 500ms delay after the game ends. Drama will come soon to this 2.1024 + space. 2.1025 + 2.1026 +_____________________________________________________________________ 2.1027 +0.03 (2006-06-21): 2.1028 + * Debriefing responds only to Esc, Enter, and keypad Enter. 2.1029 + * Compensates for refresh rates other than 60 Hz. 2.1030 + * Change log included with program. 2.1031 + * Plays sound effects for shift, rotate, land, lock, line, b2b. 2.1032 + * Bigger text using the "Aver" font. 2.1033 + * Lock delay in Marathon decreases gradually after gravity 2.1034 + surpasses 20G. 2.1035 + * Reads keys through a bitfield, allowing for custom key->vkey 2.1036 + bindings to be implemented in the future. 2.1037 + * (Internal) Does not alternate between "falling" and "landed" 2.1038 + states when resetting lock delay on slide. 2.1039 + 2.1040 +_____________________________________________________________________ 2.1041 +0.02 (2006-06-19): 2.1042 + * Speed level progresses 1/3 as fast. 2.1043 + * Added menu for selecting a gimmick (game mode) to play. 2.1044 + * Added Marathon gimmick: 2.1045 + Like Vs. but no garbage. 2.1046 + * Added 40 lines gimmick: 2.1047 + Like Marathon but ends after 40 lines. 2.1048 + * Added 3 minutes gimmick (like Marathon but ends after 10800 2.1049 + frames). 2.1050 + * Added Rhythm gimmick: 2.1051 + 20G, and if you fall below the minimum PPM, it automatically 2.1052 + locks the tetromino, and the minimum PPM increases by 10 every 2.1053 + 64 tetrominoes. 2.1054 + * Debriefing computes PPM and score. 2.1055 + * Debriefing returns to gimmick selection instead of exiting 2.1056 + the program. 2.1057 + 2.1058 +_____________________________________________________________________ 2.1059 +0.01 (2006-06-14): 2.1060 + * initial release 2.1061 + * full SRS implemented 2.1062 + * 7-piece bag randomizer 2.1063 + * 1G DAS 2.1064 + * smooth falling animation 2.1065 + * playfield size: 10w x 24h; pieces start above row 20 2.1066 + * 8 next pieces and 1 hold piece 2.1067 + * Vs. gimmick: 2.1068 + Standard S.M.G., except before every I tetromino after the first, 2.1069 + you get 4 garbage lines. 2.1070 +
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 3.2 +++ b/COPYING.txt Fri Mar 13 00:39:12 2009 -0700 3.3 @@ -0,0 +1,52 @@ 3.4 +Copying conditions for LOCKJAW: 3.5 + 3.6 +Copyright 2007 Damian Yerrick <tepples+lj@spamcop.net> 3.7 + 3.8 +This program is free software; you can redistribute it and/or 3.9 +modify it under the terms of the GNU General Public License 3.10 +as published by the Free Software Foundation; either version 2 3.11 +of the License, or (at your option) any later version. 3.12 + 3.13 +This program is distributed in the hope that it will be useful, 3.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 3.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 3.16 +GNU General Public License for more details. 3.17 + 3.18 +You should have received a copy of the GNU General Public License 3.19 +(GPL.txt) along with this program. If not, see 3.20 +<http://www.gnu.org/licenses/>. 3.21 + 3.22 +_____________________________________________________________________ 3.23 +Copying conditions for LJVorbis: 3.24 + 3.25 +Copyright 2006 Damian Yerrick 3.26 +Copyright 2002-2004 Xiph.org Foundation 3.27 + 3.28 +Redistribution and use in source and binary forms, with or without 3.29 +modification, are permitted provided that the following conditions 3.30 +are met: 3.31 + 3.32 +- Redistributions of source code must retain the above copyright 3.33 +notice, this list of conditions and the following disclaimer. 3.34 + 3.35 +- Redistributions in binary form must reproduce the above copyright 3.36 +notice, this list of conditions and the following disclaimer in the 3.37 +documentation and/or other materials provided with the distribution. 3.38 + 3.39 +- Neither the name of the Xiph.org Foundation nor the names of its 3.40 +contributors may be used to endorse or promote products derived from 3.41 +this software without specific prior written permission. 3.42 + 3.43 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 3.44 +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 3.45 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 3.46 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION 3.47 +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 3.48 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 3.49 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3.50 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3.51 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3.52 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 3.53 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3.54 + 3.55 +_____________________________________________________________________
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/GPL.txt Fri Mar 13 00:39:12 2009 -0700 4.3 @@ -0,0 +1,342 @@ 4.4 + GNU GENERAL PUBLIC LICENSE 4.5 + Version 2, June 1991 4.6 + 4.7 + Copyright (C) 1989, 1991 Free Software Foundation, Inc. 4.8 + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 4.9 + Everyone is permitted to copy and distribute verbatim copies 4.10 + of this license document, but changing it is not allowed. 4.11 + 4.12 + Preamble 4.13 + 4.14 + The licenses for most software are designed to take away your 4.15 +freedom to share and change it. By contrast, the GNU General Public 4.16 +License is intended to guarantee your freedom to share and change free 4.17 +software--to make sure the software is free for all its users. This 4.18 +General Public License applies to most of the Free Software 4.19 +Foundation's software and to any other program whose authors commit to 4.20 +using it. (Some other Free Software Foundation software is covered by 4.21 +the GNU Library General Public License instead.) You can apply it to 4.22 +your programs, too. 4.23 + 4.24 + When we speak of free software, we are referring to freedom, not 4.25 +price. Our General Public Licenses are designed to make sure that you 4.26 +have the freedom to distribute copies of free software (and charge for 4.27 +this service if you wish), that you receive source code or can get it 4.28 +if you want it, that you can change the software or use pieces of it 4.29 +in new free programs; and that you know you can do these things. 4.30 + 4.31 + To protect your rights, we need to make restrictions that forbid 4.32 +anyone to deny you these rights or to ask you to surrender the rights. 4.33 +These restrictions translate to certain responsibilities for you if you 4.34 +distribute copies of the software, or if you modify it. 4.35 + 4.36 + For example, if you distribute copies of such a program, whether 4.37 +gratis or for a fee, you must give the recipients all the rights that 4.38 +you have. You must make sure that they, too, receive or can get the 4.39 +source code. And you must show them these terms so they know their 4.40 +rights. 4.41 + 4.42 + We protect your rights with two steps: (1) copyright the software, and 4.43 +(2) offer you this license which gives you legal permission to copy, 4.44 +distribute and/or modify the software. 4.45 + 4.46 + Also, for each author's protection and ours, we want to make certain 4.47 +that everyone understands that there is no warranty for this free 4.48 +software. If the software is modified by someone else and passed on, we 4.49 +want its recipients to know that what they have is not the original, so 4.50 +that any problems introduced by others will not reflect on the original 4.51 +authors' reputations. 4.52 + 4.53 + Finally, any free program is threatened constantly by software 4.54 +patents. We wish to avoid the danger that redistributors of a free 4.55 +program will individually obtain patent licenses, in effect making the 4.56 +program proprietary. To prevent this, we have made it clear that any 4.57 +patent must be licensed for everyone's free use or not licensed at all. 4.58 + 4.59 + The precise terms and conditions for copying, distribution and 4.60 +modification follow. 4.61 + 4.62 + GNU GENERAL PUBLIC LICENSE 4.63 + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 4.64 + 4.65 + 0. This License applies to any program or other work which contains 4.66 +a notice placed by the copyright holder saying it may be distributed 4.67 +under the terms of this General Public License. The "Program", below, 4.68 +refers to any such program or work, and a "work based on the Program" 4.69 +means either the Program or any derivative work under copyright law: 4.70 +that is to say, a work containing the Program or a portion of it, 4.71 +either verbatim or with modifications and/or translated into another 4.72 +language. (Hereinafter, translation is included without limitation in 4.73 +the term "modification".) Each licensee is addressed as "you". 4.74 + 4.75 +Activities other than copying, distribution and modification are not 4.76 +covered by this License; they are outside its scope. The act of 4.77 +running the Program is not restricted, and the output from the Program 4.78 +is covered only if its contents constitute a work based on the 4.79 +Program (independent of having been made by running the Program). 4.80 +Whether that is true depends on what the Program does. 4.81 + 4.82 + 1. You may copy and distribute verbatim copies of the Program's 4.83 +source code as you receive it, in any medium, provided that you 4.84 +conspicuously and appropriately publish on each copy an appropriate 4.85 +copyright notice and disclaimer of warranty; keep intact all the 4.86 +notices that refer to this License and to the absence of any warranty; 4.87 +and give any other recipients of the Program a copy of this License 4.88 +along with the Program. 4.89 + 4.90 +You may charge a fee for the physical act of transferring a copy, and 4.91 +you may at your option offer warranty protection in exchange for a fee. 4.92 + 4.93 + 2. You may modify your copy or copies of the Program or any portion 4.94 +of it, thus forming a work based on the Program, and copy and 4.95 +distribute such modifications or work under the terms of Section 1 4.96 +above, provided that you also meet all of these conditions: 4.97 + 4.98 + a) You must cause the modified files to carry prominent notices 4.99 + stating that you changed the files and the date of any change. 4.100 + 4.101 + b) You must cause any work that you distribute or publish, that in 4.102 + whole or in part contains or is derived from the Program or any 4.103 + part thereof, to be licensed as a whole at no charge to all third 4.104 + parties under the terms of this License. 4.105 + 4.106 + c) If the modified program normally reads commands interactively 4.107 + when run, you must cause it, when started running for such 4.108 + interactive use in the most ordinary way, to print or display an 4.109 + announcement including an appropriate copyright notice and a 4.110 + notice that there is no warranty (or else, saying that you provide 4.111 + a warranty) and that users may redistribute the program under 4.112 + these conditions, and telling the user how to view a copy of this 4.113 + License. (Exception: if the Program itself is interactive but 4.114 + does not normally print such an announcement, your work based on 4.115 + the Program is not required to print an announcement.) 4.116 + 4.117 +These requirements apply to the modified work as a whole. If 4.118 +identifiable sections of that work are not derived from the Program, 4.119 +and can be reasonably considered independent and separate works in 4.120 +themselves, then this License, and its terms, do not apply to those 4.121 +sections when you distribute them as separate works. But when you 4.122 +distribute the same sections as part of a whole which is a work based 4.123 +on the Program, the distribution of the whole must be on the terms of 4.124 +this License, whose permissions for other licensees extend to the 4.125 +entire whole, and thus to each and every part regardless of who wrote it. 4.126 + 4.127 +Thus, it is not the intent of this section to claim rights or contest 4.128 +your rights to work written entirely by you; rather, the intent is to 4.129 +exercise the right to control the distribution of derivative or 4.130 +collective works based on the Program. 4.131 + 4.132 +In addition, mere aggregation of another work not based on the Program 4.133 +with the Program (or with a work based on the Program) on a volume of 4.134 +a storage or distribution medium does not bring the other work under 4.135 +the scope of this License. 4.136 + 4.137 + 3. You may copy and distribute the Program (or a work based on it, 4.138 +under Section 2) in object code or executable form under the terms of 4.139 +Sections 1 and 2 above provided that you also do one of the following: 4.140 + 4.141 + a) Accompany it with the complete corresponding machine-readable 4.142 + source code, which must be distributed under the terms of Sections 4.143 + 1 and 2 above on a medium customarily used for software interchange; or, 4.144 + 4.145 + b) Accompany it with a written offer, valid for at least three 4.146 + years, to give any third party, for a charge no more than your 4.147 + cost of physically performing source distribution, a complete 4.148 + machine-readable copy of the corresponding source code, to be 4.149 + distributed under the terms of Sections 1 and 2 above on a medium 4.150 + customarily used for software interchange; or, 4.151 + 4.152 + c) Accompany it with the information you received as to the offer 4.153 + to distribute corresponding source code. (This alternative is 4.154 + allowed only for noncommercial distribution and only if you 4.155 + received the program in object code or executable form with such 4.156 + an offer, in accord with Subsection b above.) 4.157 + 4.158 +The source code for a work means the preferred form of the work for 4.159 +making modifications to it. For an executable work, complete source 4.160 +code means all the source code for all modules it contains, plus any 4.161 +associated interface definition files, plus the scripts used to 4.162 +control compilation and installation of the executable. However, as a 4.163 +special exception, the source code distributed need not include 4.164 +anything that is normally distributed (in either source or binary 4.165 +form) with the major components (compiler, kernel, and so on) of the 4.166 +operating system on which the executable runs, unless that component 4.167 +itself accompanies the executable. 4.168 + 4.169 +If distribution of executable or object code is made by offering 4.170 +access to copy from a designated place, then offering equivalent 4.171 +access to copy the source code from the same place counts as 4.172 +distribution of the source code, even though third parties are not 4.173 +compelled to copy the source along with the object code. 4.174 + 4.175 + 4. You may not copy, modify, sublicense, or distribute the Program 4.176 +except as expressly provided under this License. Any attempt 4.177 +otherwise to copy, modify, sublicense or distribute the Program is 4.178 +void, and will automatically terminate your rights under this License. 4.179 +However, parties who have received copies, or rights, from you under 4.180 +this License will not have their licenses terminated so long as such 4.181 +parties remain in full compliance. 4.182 + 4.183 + 5. You are not required to accept this License, since you have not 4.184 +signed it. However, nothing else grants you permission to modify or 4.185 +distribute the Program or its derivative works. These actions are 4.186 +prohibited by law if you do not accept this License. Therefore, by 4.187 +modifying or distributing the Program (or any work based on the 4.188 +Program), you indicate your acceptance of this License to do so, and 4.189 +all its terms and conditions for copying, distributing or modifying 4.190 +the Program or works based on it. 4.191 + 4.192 + 6. Each time you redistribute the Program (or any work based on the 4.193 +Program), the recipient automatically receives a license from the 4.194 +original licensor to copy, distribute or modify the Program subject to 4.195 +these terms and conditions. You may not impose any further 4.196 +restrictions on the recipients' exercise of the rights granted herein. 4.197 +You are not responsible for enforcing compliance by third parties to 4.198 +this License. 4.199 + 4.200 + 7. If, as a consequence of a court judgment or allegation of patent 4.201 +infringement or for any other reason (not limited to patent issues), 4.202 +conditions are imposed on you (whether by court order, agreement or 4.203 +otherwise) that contradict the conditions of this License, they do not 4.204 +excuse you from the conditions of this License. If you cannot 4.205 +distribute so as to satisfy simultaneously your obligations under this 4.206 +License and any other pertinent obligations, then as a consequence you 4.207 +may not distribute the Program at all. For example, if a patent 4.208 +license would not permit royalty-free redistribution of the Program by 4.209 +all those who receive copies directly or indirectly through you, then 4.210 +the only way you could satisfy both it and this License would be to 4.211 +refrain entirely from distribution of the Program. 4.212 + 4.213 +If any portion of this section is held invalid or unenforceable under 4.214 +any particular circumstance, the balance of the section is intended to 4.215 +apply and the section as a whole is intended to apply in other 4.216 +circumstances. 4.217 + 4.218 +It is not the purpose of this section to induce you to infringe any 4.219 +patents or other property right claims or to contest validity of any 4.220 +such claims; this section has the sole purpose of protecting the 4.221 +integrity of the free software distribution system, which is 4.222 +implemented by public license practices. Many people have made 4.223 +generous contributions to the wide range of software distributed 4.224 +through that system in reliance on consistent application of that 4.225 +system; it is up to the author/donor to decide if he or she is willing 4.226 +to distribute software through any other system and a licensee cannot 4.227 +impose that choice. 4.228 + 4.229 +This section is intended to make thoroughly clear what is believed to 4.230 +be a consequence of the rest of this License. 4.231 + 4.232 + 8. If the distribution and/or use of the Program is restricted in 4.233 +certain countries either by patents or by copyrighted interfaces, the 4.234 +original copyright holder who places the Program under this License 4.235 +may add an explicit geographical distribution limitation excluding 4.236 +those countries, so that distribution is permitted only in or among 4.237 +countries not thus excluded. In such case, this License incorporates 4.238 +the limitation as if written in the body of this License. 4.239 + 4.240 + 9. The Free Software Foundation may publish revised and/or new versions 4.241 +of the General Public License from time to time. Such new versions will 4.242 +be similar in spirit to the present version, but may differ in detail to 4.243 +address new problems or concerns. 4.244 + 4.245 +Each version is given a distinguishing version number. If the Program 4.246 +specifies a version number of this License which applies to it and "any 4.247 +later version", you have the option of following the terms and conditions 4.248 +either of that version or of any later version published by the Free 4.249 +Software Foundation. If the Program does not specify a version number of 4.250 +this License, you may choose any version ever published by the Free Software 4.251 +Foundation. 4.252 + 4.253 + 10. If you wish to incorporate parts of the Program into other free 4.254 +programs whose distribution conditions are different, write to the author 4.255 +to ask for permission. For software which is copyrighted by the Free 4.256 +Software Foundation, write to the Free Software Foundation; we sometimes 4.257 +make exceptions for this. Our decision will be guided by the two goals 4.258 +of preserving the free status of all derivatives of our free software and 4.259 +of promoting the sharing and reuse of software generally. 4.260 + 4.261 + NO WARRANTY 4.262 + 4.263 + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 4.264 +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 4.265 +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 4.266 +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 4.267 +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 4.268 +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 4.269 +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 4.270 +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 4.271 +REPAIR OR CORRECTION. 4.272 + 4.273 + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 4.274 +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 4.275 +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 4.276 +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 4.277 +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 4.278 +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 4.279 +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 4.280 +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 4.281 +POSSIBILITY OF SUCH DAMAGES. 4.282 + 4.283 + END OF TERMS AND CONDITIONS 4.284 + 4.285 + How to Apply These Terms to Your New Programs 4.286 + 4.287 + If you develop a new program, and you want it to be of the greatest 4.288 +possible use to the public, the best way to achieve this is to make it 4.289 +free software which everyone can redistribute and change under these terms. 4.290 + 4.291 + To do so, attach the following notices to the program. It is safest 4.292 +to attach them to the start of each source file to most effectively 4.293 +convey the exclusion of warranty; and each file should have at least 4.294 +the "copyright" line and a pointer to where the full notice is found. 4.295 + 4.296 + <one line to give the program's name and a brief idea of what it does.> 4.297 + Copyright (C) <year> <name of author> 4.298 + 4.299 + This program is free software; you can redistribute it and/or modify 4.300 + it under the terms of the GNU General Public License as published by 4.301 + the Free Software Foundation; either version 2 of the License, or 4.302 + (at your option) any later version. 4.303 + 4.304 + This program is distributed in the hope that it will be useful, 4.305 + but WITHOUT ANY WARRANTY; without even the implied warranty of 4.306 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 4.307 + GNU General Public License for more details. 4.308 + 4.309 + You should have received a copy of the GNU General Public License 4.310 + along with this program; if not, write to the Free Software 4.311 + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 4.312 + 4.313 + 4.314 +Also add information on how to contact you by electronic and paper mail. 4.315 + 4.316 +If the program is interactive, make it output a short notice like this 4.317 +when it starts in an interactive mode: 4.318 + 4.319 + Gnomovision version 69, Copyright (C) year name of author 4.320 + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 4.321 + This is free software, and you are welcome to redistribute it 4.322 + under certain conditions; type `show c' for details. 4.323 + 4.324 +The hypothetical commands `show w' and `show c' should show the appropriate 4.325 +parts of the General Public License. Of course, the commands you use may 4.326 +be called something other than `show w' and `show c'; they could even be 4.327 +mouse-clicks or menu items--whatever suits your program. 4.328 + 4.329 +You should also get your employer (if you work as a programmer) or your 4.330 +school, if any, to sign a "copyright disclaimer" for the program, if 4.331 +necessary. Here is a sample; alter the names: 4.332 + 4.333 + Yoyodyne, Inc., hereby disclaims all copyright interest in the program 4.334 + `Gnomovision' (which makes passes at compilers) written by James Hacker. 4.335 + 4.336 + <signature of Ty Coon>, 1 April 1989 4.337 + Ty Coon, President of Vice 4.338 + 4.339 +This General Public License does not permit incorporating your program into 4.340 +proprietary programs. If your program is a subroutine library, you may 4.341 +consider it more useful to permit linking proprietary applications with the 4.342 +library. If this is what you want to do, use the GNU Library General 4.343 +Public License instead of this License. 4.344 + 4.345 +
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/README.html Fri Mar 13 00:39:12 2009 -0700 5.3 @@ -0,0 +1,546 @@ 5.4 +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 5.5 +<html lang="en"> 5.6 +<head> 5.7 +<title>LOCKJAW - About</title> 5.8 +<link rel="stylesheet" type="text/css" href="docs/ljhtml.css"> 5.9 +<link rel="icon" href="docs/appicon.ico" type="image/x-icon"> 5.10 +<link rel="shortcut icon" href="docs/appicon.ico" type="image/x-icon"> 5.11 +<meta http-equiv="Content-type" content="text/html;charset=iso-8859-1"> 5.12 +</head><body> 5.13 +<h1><img src="docs/ljlogo192.png" alt="LOCKJAW Tetromino Game:"> About</h1> 5.14 +<ul id="linkbar"> 5.15 +<li><a href="http://www.pineight.com/lj/">Home</a></li> 5.16 +<li>About</li> 5.17 +<li><a href="http://www.pineight.com/lj/dl">Download</a></li> 5.18 +</ul> 5.19 +<div id="content"> 5.20 +<p> 5.21 +<a href="#installing">Installing</a> | <a href="#skin">Skinning</a> | <a href="#controls">Controls</a> | <a href="#scoring">Scoring</a> | <a href="#scenario">Scenario</a> | <a href="#options">Options</a> | <a href="#discussion">Discussion</a> | <a href="#legal">Legal</a> 5.22 +</p><p> 5.23 +LOCKJAW is a <a href="http://www.gnu.org/philosophy/free-sw.html">free software</a> implementation of the so-called Soviet Mind Game, a highly popular computer puzzle game that involves guiding tetrominoes into neat stacks in a well. This game was designed in the mid-1980s by Russian game designer Alexey Pajitnov and was first implemented in a software product called <a href="http://en.wikipedia.org/wiki/Tetris">TETRIS</a>®.<a href="#trademarks">*</a> Other products implementing the Soviet Mind Game include 5.24 +<a href="http://quadra.sourceforge.net/">Quadra</a>, 5.25 +<a href="http://abrick.sourceforge.net/">Abandoned Bricks</a>, 5.26 +<a href="http://ksirtet.sourceforge.net/">KSirtet</a>, 5.27 +<a href="http://fbg.sourceforge.net/">F.B.G.</a>, 5.28 +<a href="http://www.neave.com/games/nblox/">N-Blox</a>, and 5.29 +<a href="http://www.tetrisconcept.com/forum/viewtopic.php?t=54">Heboris</a>. While originally developed to parody the behavior of a few notorious implementations of the Soviet Mind Game, LOCKJAW is now a platform for research into the properties of the game, into the effects of rule variations, and into the capabilities of the human mind to react. 5.30 +</p><p> 5.31 +<a href="http://en.wikipedia.org/wiki/Tetromino">Tetrominoes</a> are geometric shapes made of four connected square blocks. There are seven distinct tetrominoes, shaped roughly like letters of the Latin alphabet: 5.32 +</p><p> 5.33 +<img src="docs/ijlo-stz.png" alt="(Illustration of the seven tetrominoes)"><br> 5.34 +Tetrominoes. Top row: I, J, L, O. Bottom row: S, T, Z. 5.35 +</p><p style="size: 80%"> 5.36 +Some products spell the term as "tetramino", "tetrimino", or "tetrad". 5.37 +</p> 5.38 +<div class="screenshot"> 5.39 +<img src="docs/ljsnap033.png" alt="(Game screenshot)" width="400" height="300" usemap="#ljsnap018_map"> 5.40 +<p> 5.41 +LOCKJAW gameplay screen. Hover over objects to see their names. 5.42 +</p> 5.43 +<map name="ljsnap018_map"> 5.44 +<area shape="rect" coords="12,12,48,36" alt="Hold piece" title="Hold piece" style="border: 1px solid white"> 5.45 +<area shape="rect" coords="48,0,192,36" alt="Status" title="Status"> 5.46 +<area shape="rect" coords="96,48,132,108" alt="Falling piece" title="Falling piece"> 5.47 +<area shape="rect" coords="96,168,132,216" alt="Shadow" title="Shadow"> 5.48 +<area shape="rect" coords="12,36,132,276" alt="Well" title="Well"> 5.49 +<area shape="rect" coords="36,276,108,300" alt="Game mode" title="Game mode"> 5.50 +<area shape="rect" coords="144,36,192,252" alt="Next pieces" title="Next pieces"> 5.51 +<area shape="rect" coords="216,60,360,240" alt="Gus" title="Gus"> 5.52 +</map> 5.53 +</div> 5.54 + 5.55 +<p> 5.56 +The well is 10 blocks wide by 20 rows high. (There are four out-of-bounds rows above the top of the visible portion for a total of 24 rows.) The player can rotate and shift the tetrominoes as they fall in order to pack them tightly into the well. If a tetromino lands on the floor or other blocks and remains motionless for half a second, it locks into place, and the next tetromino begins to fall. The next few tetrominoes to fall are displayed in a queue to the right of the playfield. At any time, the player can swap the falling tetromino with the one in the hold box above the playfield, but a tetromino that has been swapped out cannot be immediately swapped back in. The gray blocks below the falling tetromino are the shadow, which shows where the tetromino will land. 5.57 +</p><p> 5.58 +A "line", or a complete row of blocks across the well, will disappear, and everything above it moves down by one row. But if the well fills so high that a tetromino is placed entirely out of bounds or in a position such that the next tetromino does not have room to enter, the player "tops out" and the game is over. So the goal is to stay alive by making more lines. 5.59 +</p> 5.60 + 5.61 +<h2><a name="installing">Installing</a></h2> 5.62 +<p> 5.63 +The executable included with the official distribution is designed for Microsoft Windows systems. Unzip it into a folder on your hard disk or USB memory card. It ordinarily writes preferences and logs to the current directory, which is most often the folder containing <code>lj.exe</code>. (If you do not want to allow users to write to the program's folder, place an empty file called <code>installed.ini</code> in the same folder as <code>lj.exe</code>. This will make Game Keys and Options write settings to the user's application data folder, commonly <code>C:\Documents and Settings\Gus\Application Data\Pin Eight\Lockjaw</code>.) 5.64 +</p><p> 5.65 +The files <code>lj.exe</code>, <code>lj.dat</code>, <code>ljblocks.bmp</code>, and <code>alleg42.dll</code> are required to play. (If you get an error about <code>alleg42.dll</code>, see the instructions on the LOCKJAW download page.) To start the game, run <code>lj.exe</code> in Windows Explorer. 5.66 +</p><p> 5.67 +The program should work on any PC running Microsoft Windows 98 or newer operating system with an 800x600 pixel display at 16-bit or greater color depth and DirectX 7 software installed. 5.68 +Its source code is portable to any platform that supports the <a href="http://alleg.sourceforge.net/">Allegro library</a>, but it is tested only on Microsoft Windows because the author has access only to machines that run Windows. 5.69 +Occasionally, people manage to build and run it on other operating systems such as GNU/Linux or Mac OS X; if you are willing to maintain a port, <a href="http://www.pineight.com/contact/">get in touch with the author</a>. 5.70 +</p><p> 5.71 +For the handheld version, you only need the file <code>lj.gba</code> or <code>lj.nds</code>. Due to limitations in the DS homebrew operating system, the DS version always acts installed, writing user files to the folder <code>/data/lockjaw</code> on the memory card. 5.72 +</p><p> 5.73 + 5.74 +<h3>Installing from source</h3> 5.75 +<p> 5.76 +To recompile the program, such as if you are testing a patch or porting it to another system: 5.77 +</p><ol> 5.78 +<li>Install GNU Coreutils and GNU Make. These are packaged as MSYS from <a href="http://www.mingw.org/MinGWiki/index.php/GettingStarted">MinGW.org</a></li> 5.79 +<li>Install a GCC toolchain. This is packaged as MinGW from <a href="http://www.mingw.org/MinGWiki/index.php/GettingStarted">MinGW.org</a></li> 5.80 +<li>Install Allegro 4.2.1 from <a href="http://alleg.sourceforge.net/">SourceForge.net</a></li> 5.81 +<li>Install JPGalleg 2.5 from <a href="http://www.ecplusplus.com/index.php?page=projects&pid=1">Enhanced Creations++</a></li> 5.82 +<li>Install DUMB 0.9.3 from <a href="http://dumb.sourceforge.net/">SourceForge.net</a><br > 5.83 +(Editor's note: DUMB has a dumb license, but section 6 allows relicensing under GNU GPL. Update: It appears <a href="http://packages.debian.org/changelogs/pool/main/libd/libdumb/libdumb_0.9.3-5/libaldmb1.copyright">Debian got the author to relicense it under a straight zlib style license</a>.)</li> 5.84 +<li>Open a command prompt and <kbd>cd</kbd> to the folder containing the file <code>makefile</code></li> 5.85 +<li>Type <code>make</code></li> 5.86 +</ol><p> 5.87 +Lardarse has written a detailed guide of how to recompile the program on Microsoft Windows OS. This guide is included with the source code inside the "docs" folder. 5.88 +</p><p> 5.89 +To recompile the program for Game Boy Advance or Nintendo DS: 5.90 +</p><ol> 5.91 +<li>Install devkitARM, libgba, libnds, and MSYS using devkitPro Updater at <a href="http://www.devkitpro.org/">devkitPro.org</a>.</li> 5.92 +<li>Install a GCC toolchain, such as MinGW from <a href="http://www.mingw.org/MinGWiki/index.php/GettingStarted">MinGW.org</a></li> 5.93 +<li>Open a command prompt and <kbd>cd</kbd> to the folder containing the file <code>gbamakefile</code> or <code>dsmakefile</code></li> 5.94 +<li>Type <code>make -f gbamakefile</code> or <code>make -f dsmakefile</code></li> 5.95 +</ol><p> 5.96 +To recompile all three ports, type <code>make all</code> 5.97 +</p><p> 5.98 +TIP: On GNU/Linux, Windows, and several other platforms, GNU Make can compile multiple files in parallel. This can speed up a large rebuild by allowing GCC to compile one file while reading another from disk. Add <code>-j2</code> to the end of a Make command line to compile two files at once, or if you have a dual-core CPU, add <code>-j3</code> to have GCC run one core, the other core, and the disk at the same time. 5.99 +</p> 5.100 + 5.101 +<h2><a name="skin">Skinning</a></h2> 5.102 +<p> 5.103 +You can customize the appearance of LOCKJAW Tetromino Game for PC by using Notepad or any other text editor to create a skin description that lists the images that shall be used. This skin description should be placed in a text file whose name ends in <code>.skin</code>. (You can create and edit <code>.ini</code> and <code>.skin</code> files using Notepad, Notepad++, vi, Emacs, or any other plain text editor.) LOCKJAW recognizes the following commands in a skin description: 5.104 +</p><dl> 5.105 +<dt><code>ljblocksSRS=<i>image</i></code></dt> 5.106 +<dd>A grid of block images, used in bounding-box rotation systems (SRS and TOD M4). The image's size size should be 8 columns by 4 rows (usually 192x96 pixels) or 8 columns by 6 rows (usually 192x144 pixels). Rows 1 and 2 are for blocks in the well, and rows 3 and 4 are for the falling piece. If rows 5 and 6 are present, the shadow will use those; otherwise, it will use rows 3 and 4. If <tt>blkW</tt> or <tt>blkH</tt> is present, the program uses that size instead of 24x24.</dd> 5.107 +<dt><code>ljblocksSega=<i>image</i></code></dt> 5.108 +<dd>Same as <code>ljblocksSRS</code>, but used in other rotation systems.</dd> 5.109 +<dt><code>ljconnSRS=<i>image</i></code></dt> 5.110 +<dd>An image 8 rows and 8 columns in size (usually 192x192 pixels), containing an O tetromino of each color. The skin loader cuts this up to form tetrominoes with the blocks drawn connected, used for blocks in the well and the falling piece (not the shadow or empty areas of the well) in bounding-box rotation systems. If this file is not present, <code>ljblocksSRS</code> will be used instead.</dd> 5.111 +<dt><code>ljconnSega=<i>image</i></code> 5.112 +<dd>Same as <code>ljconnSRS</code>, but used in other rotation systems. If this file is not present, <code>ljblocksSega</code> will be used instead.</dd> 5.113 +<dt><code>color=<i>#RRGGBB</i></code></dt> 5.114 +<dd>A 3- or 6-digit hexadecimal color (e.g. <code>#ABC</code> or <code>#D0FFE3</code>) for text in the menus and during the game. If not present, the game will use black.</dd> 5.115 +<dt><code>bgcolor=<i>#RRGGBB</i></code></dt> 5.116 +<dd>A color for the menus' background. If not present, the game will use white.</dd> 5.117 +<dt><code>hilitecolor=<i>#RRGGBB</i></code></dt> 5.118 +<dd>A color for the background of highlighted text. If not present, the game will use pale yellow (#FFC).</dd> 5.119 +<dt><code>pfcolor=<i>#RRGGBB</i></code></dt> 5.120 +<dd>A color for text in front of the playfield. If not present, the game will use white.</dd> 5.121 +<dt><code>pfbgcolor=<i>#RRGGBB</i></code></dt> 5.122 +<dd>An "average" color for the playfield, to be displayed behind the pause screen and during the game over animation. If not present, the game will use black.</dd> 5.123 +<dt><code>ljbg=<i>#RRGGBB</i></code></dt> 5.124 +<dd>An 800x600 pixel image to be displayed behind the game. If this file is not 800x600 pixels, the image will be resized (sloppily) after being loaded. If this file is not present, the game will use a plain backdrop of the same color as <code>bgcolor</code>.</dd> 5.125 +<dt><code>blkW=<i>length</i></code></dt> 5.126 +<dd>The width in pixels of each block in the <tt>ljblocks</tt> and <tt>ljconn</tt>, if it is not 24 pixels.</dd> 5.127 +<dt><code>blkH=<i>length</i></code></dt> 5.128 +<dd>The height in pixels of each block in the <tt>ljblocks</tt> and <tt>ljconn</tt>, if it is not 24 pixels. This is allowed to differ from <code>blkW</code></dd> 5.129 +<dt><code>transparentPF=<i>boolean</i></code></dt> 5.130 +<dd>If this is set to the value 0, tile 0 of <code>ljblocksSRS</code> covers up the background within the playfield. If this is set to nonzero, the playfield background shows through.</dd> 5.131 +<dt><code>bgm=<i>music file</i></code></dt> 5.132 +<dd>A music file in Vorbis format (<code>.ogg</code>) or tracker format (<code>.mod</code>, <code>.s3m</code>, <code>.xm</code>, or <code>.it</code>) to be played during the game. You can create Vorbis format files by extracting audio from your CDs to <code>.wav</code> format using <a href="http://sourceforge.net/projects/cdexos/">CDex software</a> and converting them with <a href="http://www.rarewares.org/ogg.html">OggDropXPd software</a>. You can download tracker format files from <a href="http://www.modarchive.com/">The Mod Archive</a> or create them yourself using the <a href="http://lpchip.com/modplug/viewtopic.php?t=18">OpenMPT music editor</a>, the continuation of MODPlug Tracker. If this file is not present, the game will not play music.</dd> 5.133 +<dt><code>bgmLoopPoint=<i>sampleNumber</i></code></dt> 5.134 +<dd>For music in Vorbis format, sets the sample at which playback restarts once the music file ends. For example, if your .ogg file is 44100 Hz, 441000 represents rewinding to 10 seconds after the start. Has no effect with tracker format music, which specifies its own loop point, or Rhythm speed curve, whose music isn't supposed to loop anyway.</dd> 5.135 +<dt><code>bgmRhythm=<i>music file</i></code></dt> 5.136 +<dd>Like <code>bgm</code>, but used in the Rhythm speed curve. Music tempo should have 64 beats of 60 BPM, 64 beats of 70 BPM, 64 beats of 80 BPM, etc.</dd> 5.137 +<dt><code>bgmReadyGo=<i>Boolean</i></code></dt> 5.138 +<dd>If this is set to the value 0, the background music starts after the "Ready Go" sequence. within the playfield. this is set to nonzero, the background music plays during the "Ready Go" sequence.</dd> 5.139 +<dt><code>bgmVolume=<i>volume</i></code></dt> 5.140 +<dd>Sets the volume of the music to balance it against the sound effects, where 256 is full volume. If not specified, uses 128; 1 <abbr title="decibel">dB</abbr> louder than this would be 144.</dd> 5.141 +<dt><code>shiftScale=<i>Boolean</i></code></dt> 5.142 +<dd>When set to 1, moving the falling piece sideways produces sound at a different pitch based on how far the piece is from the left side. (PC version only)</dd> 5.143 +<dt>baseX=<i>distance</i></dt> 5.144 +<dd>This moves all gameplay graphics to the left (0), middle (200), or right (400) of the screen.</dd> 5.145 +<dt>nextPos=<i>style number</i></dt> 5.146 +<dd>Controls where the next tetrominoes are displayed. Up to eight can fit to the right of the well, or up to 3 can fit above the well. A value of 0 places the pieces on the right; 2 places them on the top.</dd> 5.147 +<dt><code>wndW=<i>length</i></code></dt> 5.148 +<dd>Sets the width of the game window in pixels.</dd> 5.149 +<dt><code>wndH=<i>length</i></code></dt> 5.150 +<dd>Sets the height of the game window in pixels.</dd> 5.151 +</dl><p> 5.152 +You can create more than one skin description file and then switch among them by using the "Skin..." command at the main menu, which produces the following command in <code>lj.ini</code>: 5.153 +</p><dl> 5.154 +<dt><code>Skin=<i>skin description file</i></code></dt> 5.155 +<dd>A skin description file. If the skin is not present, the game uses preset file names (<code>ljblocks.bmp</code>, <code>ljblocks-sega.bmp</code>, <code>ljconn.bmp</code>, <code>ljconn-sega.bmp</code>, <code>ljbg.jpg</code>, and <code>bgm.s3m</code>).</dd> 5.156 +</dl><p> 5.157 +Images can be in Windows bitmap (<code>.bmp</code>), PC-Paintbrush (<code>.pcx</code>), Truevision TGA (<code>.tga</code>), or JFIF/JPEG (<code>.jpg</code>) format. All color depths should be supported. Paths are interpreted relative to the folder containing the <code>.skin</code> file, that is, specifying <code>ljblocksSega=Gradient Blocks.bmp</code> will try to pull <code>Gradient Blocks.bmp</code> from the skin's folder. If you want to create a folder structure inside your skin package, it is best to use forward slashes (<code>'/'</code>) instead of backslashes (<code>'\'</code>) so that users of Mac OS X and GNU/Linux will be able to use your skin. 5.158 +</p><p> 5.159 +Example image and sound files are located in <code>lj-contrib.zip</code>, available from the <a href="http://www.pineight.com/lj/dl#contrib">Skins section of the download page</a>. You can also customize the sound effects by using <a href="http://www.allegro.cc/depot/Grabber/">Allegro Grabber software</a> to edit <code>lj.dat</code>. 5.160 +</p> 5.161 + 5.162 +<h2><a name="controls">Controls</a></h2> 5.163 +<h3>PC</h3> 5.164 +<p> 5.165 +The controls in LOCKJAW Tetromino Game for PC are initially set as follows: 5.166 +</p> 5.167 +<dl> 5.168 +<dt>← Shift left</dt><dd>Left arrow key</dd> 5.169 +<dt>→ Shift right</dt><dd>Right arrow key</dd> 5.170 +<dt>↓ Soft drop</dt><dd>Down arrow key</dd> 5.171 +<dt><u>↓</u> Hard drop</dt><dd>Up arrow key</dd> 5.172 +<dt>↰ Rotate left</dt><dd>Z, C</dd> 5.173 +<dt>↱ Rotate right</dt><dd>X</dd> 5.174 +<dt>↶ Rotate twice</dt><dd>W</dd> 5.175 +<dt>↖ Hold piece</dt><dd>S, D</dd> 5.176 +<dt>⇤ Shift far left</dt><dd>Q</dd> 5.177 +<dt>⇥ Shift far right</dt><dd>E</dd> 5.178 +<dt><u>↓</u> Firm drop</dt><dd>Enter</dd> 5.179 +</dl><p> 5.180 +Controls are configurable to the keyboard or any compatible joystick. (The key labeled "Item" is not used in single-player, and there is no multiplayer yet.) From the main menu, choose "Game Keys..." and then press the keys in order as prompted. If you don't want to bind a key or button to a given function, press a key that you won't use. The key bindings are saved to the file <code>lj-keys.043</code>; if they become unusable, you can delete this file to reset them to the initial settings. 5.181 +</p><p> 5.182 +Some controls during game play are hard-coded to keyboard keys: 5.183 +</p><ul> 5.184 +<li>Esc pauses and resumes the game. Holding Esc for one second stops the game and goes to a result screen.</li> 5.185 +<li>[ (left bracket) starts and stops demo recording, and ] (right bracket) starts and stops demo playback. An icon for stop, record, or play appears in the upper left corner of the window. The demo is saved to a file called <code>demo.ljm</code>. <strong>Caution:</strong> Demos recorded on one version of LOCKJAW may not play correctly on another version. Recording another demo will overwrite the last demo, so make sure to rename the demo when you record a good one.</li> 5.186 +</ul><p> 5.187 +In the menus, Esc acts as Rotate Left, and Enter acts as Rotate Right. In all screens, Print Screen (F13) saves a copy of the display to the file <code>ljsnap.bmp</code>. 5.188 +</p> 5.189 + 5.190 +<h3>GBA and DS</h3> 5.191 +<p> 5.192 +Controls in the Game Boy Advance and Nintendo DS versions are hardcoded as follows: 5.193 +</p><dl> 5.194 +<dt>← Shift left</dt><dd>Left on Control Pad</dd> 5.195 +<dt>→ Shift right</dt><dd>Right on Control Pad</dd> 5.196 +<dt>↓ Soft drop</dt><dd>Down on Control Pad</dd> 5.197 +<dt><u>↓</u> Hard drop</dt><dd>Up on Control Pad</dd> 5.198 +<dt>↰ Rotate left</dt><dd>B Button</dd> 5.199 +<dt>↱ Rotate right</dt><dd>A Button</dd> 5.200 +<dt>↖ Hold piece</dt><dd>L Button</dd> 5.201 +</dl><p> 5.202 +In the options menu, the B and A Buttons change screens, and the Start Button starts the game. Pressing A on the last screen of options also starts the game. Start pauses and resumes the game. 5.203 +</p> 5.204 + 5.205 +<h3>Movement features</h3> 5.206 +<p> 5.207 +Unlike some other <abbr title="Soviet Mind Game">S.M.G.</abbr> implementations, LOCKJAW features Initial Actions. Holding a rotate button while a tetromino enters the playfield will cause the action to be performed the moment the tetromino enters. This is important for fast play. In addition, the hold key works at any time; if a piece is not falling yet, it will swap the hold piece with the next piece. 5.208 +</p><p> 5.209 +LOCKJAW also implements the so-called <a href="http://www.tetrisconcept.com/wiki/index.php?title=SRS">Super Rotation System (SRS)</a>, which allows a tetromino to rotate around obstacles for more mobility across the pile. Some players abuse SRS by <a href="http://infinitespin.ytmnd.com/">rotating a piece in place repeatedly</a>, but this will result in poorer scores in timed gimmicks. To replace this behavior with the "Sega style" behavior used in Arika's <i>Tetris The Grand Master 3: Terror-Instinct</i>, change the <a href="#options">Options described below</a>. 5.210 +</p> 5.211 + 5.212 +<h2><a name="scoring">Scoring</a></h2> 5.213 +<p> 5.214 +LOCKJAW Tetromino Game allows the player to choose among several methods of calculating the score for a line clear. 5.215 +The terms "single", "double", "triple", and "home run" refer to clearing 1, 2, 3, or 4 lines with one tetromino. 5.216 +(A "home run" is called a "tetris" in some other games.) 5.217 +"T-spin" means rotating a T piece as it lands to fit into a tight space. 5.218 +</p><p> 5.219 +In LJ and TDS, making a bonus line clear when your last line clear was also bonus ("back-to-back homer" or "back-to-back T-spin") will produce extra points. Making a T-spin that does not clear a line has no effect on bonus state. 5.220 +</p> 5.221 + 5.222 +<h3>LJ</h3> 5.223 +<p> 5.224 +As you clear lines, you also produce garbage that depends on the number of lines that you clear with a single tetromino. In single player mode, you earn 100 points per line cleared and 200 points per line of garbage, and in multiplayer mode (which is not yet implemented), the garbage will push up the blocks in another player's well. 5.225 +</p> 5.226 +<table summary="first row: column headings"> 5.227 +<caption>LJ scoring method</caption> 5.228 +<thead> 5.229 +<tr><th> </th><th>Lines</th><th>Garbage</th><th>Score</th><th>Bonus</th></tr> 5.230 +</thead><tbody> 5.231 +<tr><td>1 line </td><td>1</td><td>0</td><td>100</td><td>No</td></tr> 5.232 +<tr><td>2 lines</td><td>2</td><td>1</td><td>400</td><td>No</td></tr> 5.233 +<tr><td>3 lines</td><td>3</td><td>2</td><td>700</td><td>No</td></tr> 5.234 +<tr><td>4 lines</td><td>4</td><td>4</td><td>1200</td><td>Yes</td></tr> 5.235 +<tr><td>1 line T-spin </td><td>1</td><td>2</td><td>500</td><td>Yes</td></tr> 5.236 +<tr><td>2 lines T-spin</td><td>2</td><td>4</td><td>1000</td><td>Yes</td></tr> 5.237 +<tr><td>3 lines T-spin</td><td>3</td><td>6</td><td>1500</td><td>Yes</td></tr> 5.238 +<tr><td>Back-to-back bonus</td><td> </td><td>1</td><td>200</td><td>Yes</td></tr> 5.239 +</tbody> 5.240 +</table><p> 5.241 +LJ has a variant called "<a href="http://en.wikipedia.org/wiki/Nerf_%28computer_gaming%29" title="nerf, v. To weaken or remove (an element of play) as a method of rebalancing a game.">nerfed</a> spin" where T-spins produce half the garbage and only 300 points per line. 5.242 +</p> 5.243 + 5.244 +<h3>Fibonacci</h3> 5.245 +<p> 5.246 +This extension of the scoring curve seen in <i>The New Tetris</i> is based on the <a href="http://en.wikipedia.org/wiki/Fibonacci_number" title="a sequence of integers where each element is the sum of the two preceding elements">Fibonacci sequence</a>. 5.247 +</p> 5.248 +<table summary="first row: column headings"> 5.249 +<caption>TNT scoring method</caption> 5.250 +<thead> 5.251 +<tr><th>Lines</th><th>Score</th></tr> 5.252 +</thead><tbody> 5.253 +<tr><td>1</td><td>100</td></tr> 5.254 +<tr><td>2</td><td>200</td></tr> 5.255 +<tr><td>3</td><td>300</td></tr> 5.256 +<tr><td>4</td><td>500</td></tr> 5.257 +<tr><td>5</td><td>800</td></tr> 5.258 +<tr><td>6</td><td>1300</td></tr> 5.259 +<tr><td>7</td><td>2100</td></tr> 5.260 +<tr><td>8</td><td>3400</td></tr> 5.261 +<tr><td>9</td><td>5500</td></tr> 5.262 +<tr><td>10</td><td>8900</td></tr> 5.263 +<tr><td>11</td><td>14400</td></tr> 5.264 +<tr><td>12</td><td>23300</td></tr> 5.265 +<tr><td>13</td><td>37700</td></tr> 5.266 +</tbody> 5.267 +</table><p> 5.268 +Further lines score 20,000 points per line. 5.269 +</p> 5.270 + 5.271 +<h3>Hotline</h3> 5.272 +<p> 5.273 +Only lines cleared on specific rows are worth points. There is no back-to-back bonus nor T-spin bonus. 5.274 +</p> 5.275 +<table summary="first row: column headings"> 5.276 +<caption>Hotline scoring method</caption> 5.277 +<thead> 5.278 +<tr><th>Row</th><th>Score</th></tr> 5.279 +</thead><tbody> 5.280 +<tr><td>5</td><td>100</td></tr> 5.281 +<tr><td>10</td><td>200</td></tr> 5.282 +<tr><td>14</td><td>300</td></tr> 5.283 +<tr><td>17</td><td>400</td></tr> 5.284 +<tr><td>19</td><td>500</td></tr> 5.285 +<tr><td>20</td><td>600</td></tr> 5.286 +</tbody> 5.287 +</table> 5.288 + 5.289 +<h3>TDS</h3> 5.290 +<p> 5.291 +Each line clear score is multiplied by a section number, computed by dividing the total number of lines cleared before this line by 10 and adding 1. Thus, lines cleared later in the game when gravity is faster are worth more than lines cleared earlier. In TDS, the section number stops increasing after 190 lines. 5.292 +</p> 5.293 +<table summary="first row: column headings"> 5.294 +<caption>TDS scoring method</caption> 5.295 +<thead> 5.296 +<tr><th> </th><th>Lines</th><th>Score</th><th>Bonus</th></tr> 5.297 +</thead><tbody> 5.298 +<tr><td>1 line </td><td>1</td><td>100 * section</td><td>No</td></tr> 5.299 +<tr><td>2 lines</td><td>2</td><td>300 * section</td><td>No</td></tr> 5.300 +<tr><td>3 lines</td><td>3</td><td>500 * section</td><td>No</td></tr> 5.301 +<tr><td>4 lines</td><td>4</td><td>800 * section</td><td>Yes</td></tr> 5.302 +<tr><td>0 lines T-spin (wall kick) </td><td>0</td><td>100 * section</td><td> </td></tr> 5.303 +<tr><td>0 lines T-spin (no wall kick)</td><td>0</td><td>400 * section</td><td> </td></tr> 5.304 +<tr><td>1 line T-spin (wall kick) </td><td>1</td><td>200 * section</td><td>Yes</td></tr> 5.305 +<tr><td>1 line T-spin (no wall kick) </td><td>1</td><td>800 * section</td><td>Yes</td></tr> 5.306 +<tr><td>2 lines T-spin </td><td>2</td><td>1200 * section</td><td>Yes</td></tr> 5.307 +<tr><td>3 lines T-spin </td><td>3</td><td>1600 * section</td><td>Yes</td></tr> 5.308 +<tr><td>Back-to-back bonus</td><td> </td><td>50% more</td><td>Yes</td></tr> 5.309 +</tbody></table> 5.310 + 5.311 +<h3>NES</h3> 5.312 +<p> 5.313 +Each line clear is multiplied by the section number, as in TDS. The section number does not stop increasing. There is no back-to-back bonus nor T-spin bonus. 5.314 +</p> 5.315 +<table summary="first row: column headings"> 5.316 +<caption>NES scoring method</caption> 5.317 +<thead> 5.318 +<tr><th>Lines</th><th>Score</th></tr> 5.319 +</thead><tbody> 5.320 +<tr><td>1</td><td>40 * section</td></tr> 5.321 +<tr><td>2</td><td>100 * section</td></tr> 5.322 +<tr><td>3</td><td>300 * section</td></tr> 5.323 +<tr><td>4</td><td>1200 * section</td></tr> 5.324 +</tbody></table> 5.325 + 5.326 +<h3>Drop scoring</h3> 5.327 +<p> 5.328 +Some games award extra points every time the tetromino lands if the player used soft drop or hard drop. LOCKJAW can simulate these. 5.329 +</p><dl> 5.330 +<dt>None</dt><dd>Award no points.</dd> 5.331 +<dt>Continuous</dt><dd>Award 1 point per row for hard drops. Award 1 point per row between when a soft drop starts when the piece lands. A soft drop that is started, then stopped, then started again, will award points only for the last soft drop.</dd> 5.332 +<dt>Drop</dt><dd>Award 1 point per row for hard drops and for soft drops, whether or not they are interrupted.</dd> 5.333 +<dt>Soft x1 Hard x2</dt><dd>Award 1 point per row for soft drops and 2 points per row for hard drops.</dd> 5.334 +</dl> 5.335 + 5.336 +<h3>lj-scores.txt</h3> 5.337 +<p> 5.338 +On the PC and DS version, each game's results are written to standard output and <code>lj-scores.txt</code> once it ends. This way, the user can copy the results to an online forum, or the user can <code>lj | something else</code> or <code>tail -f lj-scores.txt | something else</code> in order to have an external program analyze each game as it finishes. 5.339 +</p> 5.340 + 5.341 +<h2><a name="scenario">Scenarios</a></h2> 5.342 +<p> 5.343 +In the PC version, scenarios are predefined sets of rules for the game. Most scenarios leave several rules unspecified so that you can further customize them in Options. On the Play menu, you can choose from over ten preset scenarios, or you can design your own rules in Options and then activate them with Custom Game. 5.344 +</p><p> 5.345 +Future versions of LOCKJAW Tetromino Game will allow the user to edit scenarios and to use scenarios on handhelds. 5.346 +</p> 5.347 + 5.348 +<h2><a name="options">Options</a></h2> 5.349 +<p> 5.350 +In Tetris, rules change you. But in LOCKJAW, <strong>you</strong> change the rules: 5.351 +</p> 5.352 + 5.353 +<h3>Definitions</h3> 5.354 +<dl> 5.355 +<dt>Frame</dt> 5.356 +<dd>All video games are turn-based. Real-time games make each turn take a fraction of a second, where each turn lasts only one frame of animation. LOCKJAW always uses 60 frames per second, regardless of the refresh rate of the attached monitor.</dd> 5.357 +<dt>G</dt> 5.358 +<dd>1G is a velocity of 1 cell per frame, or 60 cells per second. "20G" means that tetrominoes fall through the entire height of the well in one frame.</dd> 5.359 +<dt>Hertz (Hz)</dt> 5.360 +<dd>1 Hz is a rate of 1 event per second.</dd> 5.361 +</dl> 5.362 + 5.363 +<h3><a name="gimmicks">Gimmicks</a></h3> 5.364 +<dl> 5.365 +<dt>Marathon</dt> 5.366 +<dd>The game gets faster and faster as each tetromino enters the well. Play until you top out. Some people play for lines; others play for points.</dd> 5.367 +<dt>40 lines</dt> 5.368 +<dd>Play until you clear 40 lines, or until you top out, whatever comes first. The author's record is 1:00.70.</dd> 5.369 +<dt>180 seconds</dt> 5.370 +<dd>Play for three minutes, or until you top out, whatever comes first.</dd> 5.371 +<dt>Vs. w/Items</dt> 5.372 +<dd>This mode is a joke. After the first 7 pieces you get random starting orientations, no rotation, and hidden next pieces, and the speed goes to 1G. Every time you're about to get an I tetromino, either you get 2 lines of garbage or the columns of blocks in the well are shuffled. It is debatable whether this mode is even playable. This mode exists primarily as a criticism of a similar mode in the game <i>Tetris DS</i>.</dd> 5.373 +<dt>Baboo!</dt> 5.374 +<dd>Standard S.M.G. with zero gravity, ending after 300 keypresses. Some players recommend practicing Baboo!, claiming that the way to fast play in other gimmicks involves using as few keystrokes as possible to place each tetromino.</dd> 5.375 +</dl> 5.376 + 5.377 +<h3>Well rules</h3> 5.378 +<dl> 5.379 +<dt>Well width</dt> 5.380 +<dd>Standard S.M.G. uses 10 columns; it can be set anywhere between 4 and 12.</dd> 5.381 +<dt>Well height</dt> 5.382 +<dd>Standard S.M.G. uses 20 rows; it can be decreased to 8.</dd> 5.383 +<dt>Enter above ceiling</dt> 5.384 +<dd>Normally, tetrominoes enter the well above the ceiling. But when this is turned off, tetrominoes enter inside the well.</dd> 5.385 +<dt>Speed curve</dt> 5.386 +<dd>This controls how fast tetrominoes fall: 5.387 +<ul> 5.388 +<li>Exponential is the familiar behavior where the game speeds up gradually as pieces enter the well. For the first 600 pieces, it speeds up every 30 pieces, doubling every 60. Then, the gravity hits 20G, and lock delay begins to decrease in harmonic progression: 2/(3 + 9/256 * (<i>n</i> - 609)) seconds.</li> 5.389 +<li>In Zero, tetrominoes do not fall on their own. It's pointless for setting endurance records but useful for practicing tricky stacking methods.</li> 5.390 +<li>Rhythm starts at 0G or 20G; if your play speed drops below the stated level, tetrominoes will lock on their own. This level starts at 60 tetrominoes per minute and increases by 10 every 64 beats.</li> 5.391 +<li>Master and Death are similar to Exponential and Rhythm but resemble some Japanese S.M.G. implementations: every line cleared advances the level by 1, and except at levels 99, 199, 299, etc., so does every tetromino dropped. (Death is a bit faster than starting Master at level 600, where blocks fall at 20G. Death 300+ is what it sounds like: starting Death at level 300.)</li> 5.392 +<li>NES and Game Boy speed curves approximate the behavior of the classic 8-bit games published by Nintendo. Game Boy Heart is like Game Boy but starting 100 lines in.</li> 5.393 +</ul></dd> 5.394 +<dt>Max entry delay</dt> 5.395 +<dd>When set greater than 0, there is a brief delay before each tetromino enters the playfield, and another delay when one or more lines are cleared. 5.396 +(So if you want fast play, make more lines at once.) 5.397 +During entry delay, sometimes called "ARE", the player can hold the next piece, rotate the next piece (if turned on), and charge up the autorepeat of sideways movement. 5.398 +This delay is considered a "maximum", as it decreases over the course of the game in Master and Death speed curves. 5.399 +Caution: When entry delay is on, hard drop also functions as an Initial Action.</dd> 5.400 +<dt>Piece set</dt> 5.401 +<dd>This selects which pieces are used: 5.402 +<ul> 5.403 +<li>"All pieces" deals all tetrominoes, plus trominoes of 3 blocks (I3, L3) and the domino of 2 blocks (I2). In history randomizer, it starts with I, J, L, T, or L3.</li> 5.404 +<li>"Tetrominoes" includes the seven pieces with four blocks (I, J, L, O, S, T, and Z). In history randomizer, it starts with I, J, L, or T.</li> 5.405 +<li>"No sticks" is one stick short of a bundle because it generates all tetrominoes other than I, which is sometimes called a "stick" or "bar". It's useful for practicing "push" and T-spins. In history randomizer, it starts with J, L, or T.</li> 5.406 +<li>"SZSZ" deals only S and Z tetrominoes. History randomizers will deal an alternating sequence (S, Z, S, Z). It was used in a <a href="http://www2.math.uic.edu/~burgiel/Tetris/explanation.html">proof that S.M.G. with the Memoryless randomizer cannot be played forever</a>.</li> 5.407 +<li>"iCheat™" generates <em>only</em> I tetrominoes, simulating cheat modes on some other games.</li> 5.408 +<li>"T-Party" generates only T tetrominoes.</li> 5.409 +</ul></dd> 5.410 +<dt>Randomizer</dt> 5.411 +<dd>This selects the function used to shuffle the pieces: 5.412 +<ul> 5.413 +<li>"Memoryless" has a more or less equal probability of choosing each piece at any given time, as seen in most <abbr title="Soviet Mind Game">S.M.G.</abbr> implementations (including Tetris brand games from before 2001).</li> 5.414 +<li>"Bag" treats the pieces as sets and deals a randomized sequence of all different pieces, as if drawing them from a bag, before the next set. Advanced players like to "count cards", keeping track of the "seams" between bags of seven tetrominoes. This randomizer has a weakness: it can easily be <a href="http://www.tetrisconcept.com/wiki/index.php?title=Playing_forever">played forever</a>.</li> 5.415 +<li>"Bag + 1" is like Bag but tosses a duplicate piece into each bag.</li> 5.416 +<li>"Double bag" adds an extra copy of each piece to the bag, as a compromise between the even distribution of Bag and unpredictability of Memoryless.</li> 5.417 +<li>"Strict history" will not deal a piece that was recently dealt. For tetrominoes, it will not deal one of the previous four. This way the player never gets a clump of all the same piece.</li> 5.418 +<li>"History 6 rolls" is similar to strict history but sometimes (about 3.5%) generates repeats of recent pieces.</li> 5.419 +</ul></dd> 5.420 +</dl> 5.421 + 5.422 +<h3>Movement rules</h3> 5.423 +<dl> 5.424 +<dt>Hold piece</dt> 5.425 +<dd>Controls the behavior of the hold piece. Empty leaves the hold box empty at the start of the game; the first time the player uses hold piece, the falling piece goes to the hold box, and the next piece enters instead of the hold piece. Random fills the hold piece with a random piece at the start of the game. Hold to next holds to the next piece box instead of the hold box. Off disables hold entirely.</dd> 5.426 +<dt>Rotation system</dt> 5.427 +<dd>In different rotation systems, tetrominoes may enter the field at different positions, and they behave differently when rotated against the walls and the other blocks. This option can be set to SRS (easiest), TOD M4 (like SRS in free space but less forgiving in wall kicks), Arika (like Sega with basic wall kicks), Sega or NES or Game Boy (no kicks), or Tengen (strict, with even stranger twists). Apart from one set of graphics for SRS and TOD M4 and one set for other systems, this option does not affect the colors of the tetrominoes; use the <code>.skin</code> commands to select replacement images that contain other colors. It also does not affect lockdown behavior.</dd> 5.428 +<dt>Floor kicks</dt> 5.429 +<dd>SRS and Arika rotation systems move a tetromino upward when a rotation would strike the floor or blocks under the tetromino. As this makes some tetrominoes easy to spin in place, this can detract from the immediacy of the game. This option limits the number of times each tetromino can be kicked upward.</dd> 5.430 +<dt>Lockdown</dt> 5.431 +<dd>Controls the interpretation of the delay between when each tetromino lands and when it locks. Classic disables the delay, as was the case in the oldest S.M.G. implementations; this makes slide moves more difficult. Entry reset allows only a constant amount of this delay per tetromino; instead of resetting, the lock timer pauses while the tetromino is falling. With Step reset, the delay resets every time the tetromino moves downward. With Move reset, the delay resets every time the tetromino moves at all. (Outside of Zero and Exponential speed curves, Classic behaves as Step reset.)</dd> 5.432 +<dt>Lock delay</dt> 5.433 +<dd>Controls the length of this delay. It can be set to a constant amount of time, or it can be controlled by the speed curve.</dd> 5.434 +<dt>Deep drop</dt> 5.435 +<dd>When this is on, a piece can fall past blocks in the playfield to fill appropriately shaped holes. This is potentially more controversial than the "T-spin triple" of SRS.</dd> 5.436 +</dl> 5.437 + 5.438 +<h3>Line clear rules</h3> 5.439 +<dl> 5.440 +<dt>Line clear delay</dt> 5.441 +<dd>Controls the length of the delay after a line clear. It can be set to a constant amount of time, or it can be controlled by the speed curve.</dd> 5.442 +<dt>Clear gravity</dt> 5.443 +<dd>Controls what happens to blocks after a line clear. 5.444 +Naive means that blocks move down by exactly the number of cleared lines below them, potentially causing floating blocks. 5.445 +In Sticky, tetrominoes stick together when they lock, and contiguous groups fall together. 5.446 +It's possible for a group to fall past the cleared lines, causing chain reactions. 5.447 +Sticky by color is similar to Sticky except only blocks of a single color stick together. 5.448 +Cascade treats each piece as a separate entity unless Gluing is turned on.</dd> 5.449 +<dt>Gluing</dt> 5.450 +<dd>When turned on, this controls how pieces glue themselves together after they land: 5.451 +<ul> 5.452 +<li>In Square, when the player makes a 4x4 block square out of four complete tetrominoes, it will become a large square of solid gold or silver. Pieces that have been broken by a line clear cannot make squares and are thus drawn as garbage. A line containing a row of a silver square is worth 500 bonus points; a line containing a row of a gold square is worth 1000. <strong>Caution:</strong> This mode is displayed incorrectly if the current <a href="#skin">skin</a> lacks the appropriate <code>ljconn</code> file.</li> 5.453 +<li>Sticky and Sticky by color act much like the corresponding Clear gravity settings. If a skin using connected blocks has a bright border around each piece, this can be used to draw a white border around each mass of pieces. Garbage always acts Sticky by color.</li> 5.454 +</ul> 5.455 +</dd> 5.456 +<dt>Scoring</dt> 5.457 +<dd>Choose a scoring method for line clears, as described <a href="#scoring">above</a>.</dd> 5.458 +<dt>Drop scoring</dt> 5.459 +<dd>Choose a scoring method for drops, as described <a href="#scoring">above</a>.</dd> 5.460 +<dt>T-spin detection</dt> 5.461 +<dd>Controls the definition of a T-spin used by the scoring method. When turned off, T-spins are not rewarded. When set to Immobile, a line is a T-spin line if the tetromino that completes it could not have moved left, right, or up. When set to 3-corner T, a line is a T-spin line if all of the following are true: 5.462 +<ul> 5.463 +<li>The line is completed with a T tetromino.</li> 5.464 +<li>At least 3 of the 4 boxes diagonal to the center of the tetromino are either the wall or occupied with a block.</li> 5.465 +<li>The tetromino has not shifted sideways or down by one or more spaces since it was last rotated.</li> 5.466 +</ul> 5.467 +3-corner T no kick adds an additional restriction: The rotation itself did not move the tetromino.</dd> 5.468 +<dt>Garbage</dt> 5.469 +<dd>Pushes up the lines in your playfield. In Level 1 through 4, a simulated computer opponent occasionally sends you this many lines of garbage. In Home Run Derby, every line you clear other than with a home run or a T-spin gives you garbage. In Drill and Preload Zigzag, the well is full of garbage except for the top few rows. Be prepared to rely on infinite spin for the first few lines until your skill improves.</dd> 5.470 +</dl> 5.471 + 5.472 +<h3>Control options</h3> 5.473 +<dl> 5.474 +<dt>Max sideways delay</dt> 5.475 +<dd>This controls the time the left or right key has to be held down before sideways movement begins. This can be set in 16.7 <abbr title="millisecond">ms</abbr> increments from 16.7 ms to 300 ms. 5.476 +This delay is considered a "maximum", as it decreases over the course of the game in Master and Death speed curves.</dd> 5.477 +<dt>Sideways speed</dt> 5.478 +<dd>This controls how fast the tetromino moves sideways. It can be set at Instant (tetromino reaches side instantly as soon as sideways delay expires) or 10 to 60 moves per second.</dd> 5.479 +<dt>Initial sideways motion</dt> 5.480 +<dd>When this is turned on, the tetromino can move sideways in the first frame that it appears. This affects mobility at very high speeds (e.g. Death or Rhythm).</dd> 5.481 +<dt>Initial rotation</dt> 5.482 +<dd>When this is turned on, the player can rotate a piece before it enters by holding down a rotate button.</dd> 5.483 +<dt>Allow diagonal motion</dt> 5.484 +<dd>When this is turned off, the Left, Right, Up, and Down keys will be ignored if Left or Right is pressed at the same time as Up or Down. Some players who use joysticks or gamepads claim that such "4-way" logic reduces misdrops. Keyboard players should leave it turned on, as it is necessary for "Lock on release" to work properly.</dd> 5.485 +<dt>Soft drop speed</dt> 5.486 +<dd>Controls how fast the tetromino moves downward in a soft drop. Can be set to 1G, 1/2G, or 1/3G.</dd> 5.487 +<dt>Soft drop</dt> 5.488 +<dd>Controls how tetrominoes behave when they land after being soft dropped with the Down key. "Lock" means that they will lock instantly. "Slide" allows the player to place a tetromino under an overhang in one smooth motion (move down, move sideways, then press Up to lock). "Lock on release" is similar to "Slide" but locks when the player lets go of Down.</dd> 5.489 +<dt>Hard drop</dt> 5.490 +<dd>Controls how tetrominoes behave when they land after being hard dropped with the Up key. "Lock" means that hard-dropped tetrominoes will lock instantly. "Slide" allows the player to place a tetromino under an overhang in one smooth motion (move down, move sideways, then press Down to lock). (The "Firm drop" key acts as a hard drop set to "Slide".) "Lock on release" is similar to "Slide" but locks when the player lets go of Up.</dd> 5.491 +</dl> 5.492 + 5.493 +<h3>Display options</h3> 5.494 +<p> 5.495 +The details may vary between the PC version and the GBA and DS versions: 5.496 +</p><dl> 5.497 +<dt>Shadow</dt> 5.498 +<dd>Controls how the falling tetromino and its shadow are displayed. It can show the falling tetromino along with a transparent colored shadow, opaque colored shadow, or colorless shadow. It's also possible to hide the shadow entirely or (for experts only) hide both the shadow and the falling tetromino.</dd> 5.499 +<dt>Hide blocks in well</dt> 5.500 +<dd>When turned on, hides the blocks that have locked down in the well. Useful for practicing for "invisible challenge" in another game.</dd> 5.501 +<dt>Next pieces</dt> 5.502 +<dd>Controls the number of previewed pieces displayed next to the well.</dd> 5.503 +<dt>Smooth gravity</dt> 5.504 +<dd>When this is turned off, tetrominoes fall in units of one block. When turned on, they fall pixel by pixel, and only the bottom edge of each block in a tetromino is tested for collision against the blocks in the well.</dd> 5.505 +<dt>Next above shadow</dt> 5.506 +<dd>Controls the number of previewed pieces displayed above the shadow. (PC version only)</dd> 5.507 +<dt>Drop trails</dt> 5.508 +<dd>When this is turned on, hard drops and fast piece gravity cause a piece to make a trail as it falls. It makes players feel faster at the game. (PC version only)</dd> 5.509 +<dt>Pause on task switch</dt> 5.510 +<dd>When turned on, automatically pauses the game when the player switches to another window. (PC version only; DS version always pauses on lid close)</dd> 5.511 +<dt>Record all games</dt> 5.512 +<dd>Automatically records each game to <tt>demo.ljm</tt>. Remember to rename your record-setting replays. (PC version only)</dd> 5.513 +<dt>Display</dt> 5.514 +<dd>Choose full screen or windowed mode. If windowed mode does not work, try excluding LOCKJAW from your window decoration theming program. (PC version only; GBA and DS always run in full screen)</dd> 5.515 +</dl><p> 5.516 +The PC version saves these options to <code>lj.ini</code> every time you exit the options screen. Other options not shown are also saved to <code>lj.ini</code> and can be changed with a text editor. 5.517 +</p> 5.518 + 5.519 +<h2><a name="discussion">Discussion</a></h2> 5.520 +<p> 5.521 +If you want to discuss the game, you can do so at <a href="http://www.tetrisconcept.com/forum/viewtopic.php?t=100">TetrisConcept.com forum</a>. 5.522 +</p><p> 5.523 +If you want a Tetris shot, ask your doctor. 5.524 +</p> 5.525 + 5.526 +<h2><a name="legal">Legal</a></h2> 5.527 +<div style="border: 1px solid black; padding: 1em 2em"> 5.528 +<p> 5.529 +Copyright 2006–2008 Damian Yerrick <<a href="http://www.pineight.com/contact/">tepples+lockjaw (at) spamcop (full stop) net</a>>. This manual is under the following license: 5.530 +</p><p> 5.531 +This work is provided 'as-is', <span style="font-variant: small-caps">without any express or implied warranty.</span> In no event will the authors be held liable for any damages arising from the use of this work. 5.532 +</p><p> 5.533 +Permission is granted to anyone to use this work for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 5.534 +</p><ol> 5.535 +<li>The origin of this work must not be misrepresented; you must not claim that you wrote the original work. If you use this work in a product, an acknowledgment in the product documentation would be appreciated but is not required.</li> 5.536 +<li>Altered source versions must be plainly marked as such, and must not be misrepresented as being the original work.</li> 5.537 +<li>This notice may not be removed or altered from any source distribution.</li> 5.538 +</ol><p> 5.539 +The term "source" refers to the preferred form of a work for making changes to it. 5.540 +</p> 5.541 +</div> 5.542 +<p> 5.543 +The LOCKJAW software described by this manual is distributed under the <a href="http://www.gnu.org/copyleft/gpl.html">GNU General Public License, version 2 or later</a>, <span style="font-variant: small-caps">with absolutely no warranty.</span> See <code>COPYING.txt</code> for details. 5.544 +</p><p> 5.545 +<a name="trademarks">*</a> Tetris is a trademark of The Tetris Company. The Software is not sponsored or endorsed by Alexey Pajitnov, Tetris Holding, Apple, Cloudmakers, Free Software Foundation, Microsoft, or Nintendo. 5.546 +</p> 5.547 +</div> 5.548 +</body> 5.549 +</html> 5.550 \ No newline at end of file
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 6.2 +++ b/TODO.txt Fri Mar 13 00:39:12 2009 -0700 6.3 @@ -0,0 +1,13 @@ 6.4 +As of LOCKJAW 0.42, TODO.txt is being migrated to a bug list format; 6.5 +see BUGS.txt. Not yet migrated: 6.6 + 6.7 +cdsboy wants: 6.8 + * DTET rush mode (please describe this in detail) 6.9 + * DTET lives (please describe this in detail) 6.10 + * Editable game keys presets (please describe the UI) 6.11 + 6.12 +DIGITAL wants: 6.13 + * Ability to switch tetromino colors in game (huh?) 6.14 + 6.15 +Zed0 wants: 6.16 + * Tetrinet client
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/binzip.in Fri Mar 13 00:39:12 2009 -0700 7.3 @@ -0,0 +1,25 @@ 7.4 +alleg42.dll 7.5 +lj.exe 7.6 +lj.dat 7.7 +COPYING.txt 7.8 +GPL.txt 7.9 +README.html 7.10 +CHANGES.txt 7.11 +TODO.txt 7.12 +BUGS.txt 7.13 +ljbg.jpg 7.14 +ljblocks.bmp 7.15 +ljconn.bmp 7.16 +ljblocks-sega.bmp 7.17 +ljconn-sega.bmp 7.18 +bgm.s3m 7.19 +bgm-rhythm.s3m 7.20 +sound.dat 7.21 +lj.gba 7.22 +lj.nds 7.23 +docs/ljlogo192.png 7.24 +docs/titlebarbg.png 7.25 +docs/ljsnap033.png 7.26 +docs/ijlo-stz.png 7.27 +docs/appicon.ico 7.28 +docs/ljhtml.css
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 8.2 +++ b/docs/Compiling_on_Windows.txt Fri Mar 13 00:39:12 2009 -0700 8.3 @@ -0,0 +1,103 @@ 8.4 +______________________________________________________________________ 8.5 + 8.6 +Guide to compiling LOCKJAW on Microsoft Windows 8.7 +by Lardarse and tepples 8.8 + 8.9 +You will need the following files: 8.10 + 8.11 +* Dev-C++ 5.0 Beta 8.12 + http://prdownloads.sourceforge.net/dev-cpp/devcpp-4.9.9.2_setup.exe 8.13 +* devkitPro Updater 8.14 + http://www.devkitpro.org/ 8.15 +* Allegro 4.2.0 8.16 + http://www.pineight.com/lj/all420.zip 8.17 +* Minimal DirectX 7 SDK for MinGW 8.18 + http://alleg.sourceforge.net/files/dx70_mgw.zip 8.19 +* libogg 1.1.3 8.20 + http://www.pineight.com/lj/libogg-1.1.3.zip 8.21 +* libvorbis 1.1.2 8.22 + http://www.pineight.com/lj/libvorbis-1.1.2.zip 8.23 +* DUMB 0.9.3 8.24 + http://www.pineight.com/lj/dumb-0.9.3.zip 8.25 +* JPGalleg 2.5 8.26 + http://www.pineight.com/lj/jpgalleg-2.5.tar.gz 8.27 + 8.28 +______________________________________________________________________ 8.29 +Install MinGW 8.30 + 8.31 +Put all of those files into a folder on your desktop. Run the Dev-C++ 8.32 +installer, installing into C:\Dev-Cpp, and using the default settings, 8.33 +unless you have a reason for not wanting to associate C source file 8.34 +types with Dev-C++. 8.35 + 8.36 +______________________________________________________________________ 8.37 +Install MSYS and devkitARM 8.38 + 8.39 +Run the devkitPro Updater. It will ask you if you want to get a newer 8.40 +version. Answer yes to this, and then install everything that it asks 8.41 +to, except devkitPSP and devkitPPC. MSYS should be installed into 8.42 +C:\devkitPro\msys, and just press Enter to all of the questions that 8.43 +it asks when installing it. The other parts will install without user 8.44 +intervention, unless the downloading is interrupted. 8.45 + 8.46 +______________________________________________________________________ 8.47 +Make MinGW and MSYS available 8.48 + 8.49 +Right-click My Computer and go to Properties -> Advanced -> 8.50 +Environment Variables. Add MINGDIR = C:\Dev-Cpp and make sure 8.51 +that Path starts with C:\Dev-Cpp\Bin;c:\devkitPro\msys\bin; 8.52 + 8.53 +Extract dx70_mgw.zip into C:\Dev-Cpp, over-writing files as 8.54 +necessary. Extract the other 5 files that were downloaded earlier 8.55 +into C:\, where each one will be in its own folder. Now open a 8.56 +command prompt (Start -> Run -> cmd) and enter the following commands: 8.57 + 8.58 +______________________________________________________________________ 8.59 +Make and install required libraries 8.60 + 8.61 +cd \ 8.62 +cd allegro 8.63 +fix.bat mingw32 8.64 +make 8.65 +make install 8.66 +cd \ 8.67 +cd libogg-1.1.3 8.68 +sh configure --prefix=C:\Dev-Cpp --disable-shared 8.69 +make 8.70 +make install 8.71 +cd \ 8.72 +cd libvorbis-1.1.2 8.73 +sh configure --prefix=C:\Dev-Cpp --disable-shared 8.74 +make 8.75 +make install 8.76 +cd \ 8.77 +cd dumb-0.9.3 8.78 +make 8.79 + 8.80 +This command will ask you 2 questions. Answer M to the first 8.81 +and Y to the second. 8.82 + 8.83 +make install 8.84 +cd \ 8.85 +cd jpgalleg-2.5 8.86 +fix.bat mingw32 8.87 +make 8.88 +make install 8.89 + 8.90 +At this point you can now close this window. 8.91 + 8.92 +Now open another command window and navigate to the folder where you 8.93 +have Lockjaw installed. You may prefer to use a different folder 8.94 +than your usual playing folder. If you are going to be compiling 8.95 +a lot, and you are using Windows XP, then you may want to install 8.96 +the Open Command Window Here powertoy: 8.97 +http://www.microsoft.com/windowsxp/downloads/powertoys/xppowertoys.mspx 8.98 + 8.99 +Once you are in the folder, just type in make, and within a few 8.100 +seconds, it should be compiled. The lj.exe will be roughly twice the 8.101 +size of that in the official distribution; you can fix this with UPX. 8.102 +http://upx.sourceforge.net/ 8.103 + 8.104 +Now to try to do something intersting with it... 8.105 + 8.106 +Now grab yourself a drink. You deserve it...
9.1 Binary file docs/appicon.ico has changed
10.1 Binary file docs/appicon.xcf has changed
11.1 Binary file docs/dsicon.bmp has changed
12.1 Binary file docs/favicon.ico has changed
13.1 Binary file docs/ijlo-stz.png has changed
14.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 14.2 +++ b/docs/ljhtml.css Fri Mar 13 00:39:12 2009 -0700 14.3 @@ -0,0 +1,47 @@ 14.4 +body { 14.5 + margin: 0 0 1em 0; font-family: Bitstream Vera Sans, sans-serif 14.6 +} 14.7 +h1 { 14.8 + height: 192px; margin: 0; padding: 0 1em; background: url(titlebarbg.png); color: #FFF 14.9 +} 14.10 +h1 img { 14.11 + vertical-align: middle; 14.12 +} 14.13 +#content { 14.14 + margin: 0; padding: 1em 2em; 14.15 + color: #000; background: #FFF 14.16 +} 14.17 +#linkbar { 14.18 + position: absolute; top: 1em; left: 480px; color: #fff; margin: 0; font-size: 120%; 14.19 + padding-left: 1em 14.20 +} 14.21 +#linkbar a:link { color: #99f } 14.22 +#linkbar a:visited { color: #f69 } 14.23 +#linkbar a:hover { color: #ccf } 14.24 +#linkbar a:active { color: #fff } 14.25 + 14.26 +.precis { 14.27 + margin: 2em 2em 2em 2em; 14.28 + border: 1px solid #c00; 14.29 + background: #ffc; 14.30 + padding: 1em 2em 1em 2em; 14.31 + font-size: 120%; 14.32 +} 14.33 + 14.34 +.precis-ad { 14.35 + margin: 2em 160px 2em 2em; 14.36 + border: 1px solid #c00; 14.37 + background: #ffc; 14.38 + padding: 1em 2em 1em 2em; 14.39 + font-size: 120%; 14.40 +} 14.41 + 14.42 +.screenshot { 14.43 + float: right; 14.44 + margin: 0 0 1em 2em; 14.45 + font-size: 80%; 14.46 +} 14.47 + 14.48 +img.screenshot, .screenshot img { 14.49 + border: 0px none; 14.50 +}
15.1 Binary file docs/ljlogo192.png has changed
16.1 Binary file docs/ljsnap018.png has changed
17.1 Binary file docs/titlebarbg.png has changed
18.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 18.2 +++ b/dsmakefile Fri Mar 13 00:39:12 2009 -0700 18.3 @@ -0,0 +1,136 @@ 18.4 +# Makefile for Nintendo DS version of LOCKJAW 18.5 +# 18.6 +# Copr. 2006-2007 Damian Yerrick 18.7 +# 18.8 +# This work is free software; you can redistribute it and/or modify 18.9 +# it under the terms of the GNU General Public License as published by 18.10 +# the Free Software Foundation; either version 2 of the License, or 18.11 +# (at your option) any later version. 18.12 +# 18.13 +# This program is distributed in the hope that it will be useful, 18.14 +# but WITHOUT ANY WARRANTY; without even the implied warranty of 18.15 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18.16 +# GNU General Public License for more details. 18.17 +# 18.18 +# You should have received a copy of the GNU General Public License 18.19 +# along with this program; if not, write to the Free Software 18.20 +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18.21 + 18.22 +srcdir := src 18.23 +objdir := obj/ds 18.24 +EXE := $(objdir)/ljds.elf 18.25 +EXE7 := $(objdir)/ljds7.elf 18.26 +ASSETSFILE := $(objdir)/assets.s 18.27 +ASSETS := $(srcdir)/text.chr $(srcdir)/gbablk.chr $(objdir)/vwfont.bin 18.28 +ARMGCC=arm-eabi-gcc 18.29 +ARMOBJ=arm-eabi-objcopy 18.30 +CFLAGS7=-std=gnu99 -Wall -O2 -mcpu=arm7tdmi -mtune=arm7tdmi -fomit-frame-pointer -ffast-math -mthumb-interwork 18.31 +CFLAGS9=-std=gnu99 -Wall -O2 -mcpu=arm9tdmi -mtune=arm9tdmi -fomit-frame-pointer -ffast-math -mthumb-interwork -DDEBRIEF_SHORT -DHAS_FOPEN 18.32 +LDLIBS9=-lfat -lnds9 18.33 +NDSLIB_INCLUDE=$(DEVKITPRO)/libnds/include 18.34 +NDSLIB_LIB=$(DEVKITPRO)/libnds/lib 18.35 +DEPOBJS := \ 18.36 +$(objdir)/ljds.o $(objdir)/ljplay.o $(objdir)/lj.o \ 18.37 +$(objdir)/gimmicks.o $(objdir)/wktables.o $(objdir)/macro.o \ 18.38 +$(objdir)/gbaopt.o $(objdir)/speed.o $(objdir)/options.o \ 18.39 +$(objdir)/fontdraw.o $(objdir)/dssleep.o $(objdir)/random.o \ 18.40 +$(objdir)/ljlocale.o $(objdir)/debrief.o $(objdir)/dsdebrief.o \ 18.41 +$(objdir)/gbamenus.o $(objdir)/ljpath.o $(objdir)/dsjoy.o 18.42 +OBJS := $(DEPOBJS) $(ASSETSFILE) 18.43 +EMU := start 18.44 +GAMEICON := docs/dsicon.bmp 18.45 + 18.46 +CC := gcc 18.47 +CFLAGS := 18.48 + 18.49 +# The wintermute-approved way to ensure devkitARM is in the PATH 18.50 +ifeq ($(strip $(DEVKITPRO)),) 18.51 +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>devkitPro) 18.52 +endif 18.53 +ifeq ($(strip $(DEVKITARM)),) 18.54 +$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM) 18.55 +endif 18.56 +export PATH := $(DEVKITARM)/bin:$(PATH) 18.57 + 18.58 +.PHONY: run clean cf 18.59 + 18.60 +run: lj.nds 18.61 + $(EMU) $< 18.62 + 18.63 +lj.nds: $(objdir)/ljds.bin $(objdir)/ljds7.bin $(GAMEICON) 18.64 + ndstool -c $@ -9 $(objdir)/ljds.bin -7 $(objdir)/ljds7.bin -b $(GAMEICON) "LOCKJAW DS;The Soviet Mind Game" 18.65 + 18.66 +%.nds.gba: %.nds 18.67 + dsbuild $< -o $@ 18.68 + 18.69 +%.bin: %.elf 18.70 + $(ARMOBJ) -O binary $< $@ 18.71 + 18.72 +$(EXE): $(OBJS) 18.73 + $(ARMGCC) -g -mthumb-interwork -mno-fpu -specs=ds_arm9.specs $^ -L$(NDSLIB_LIB) $(LDLIBS9) -o $@ 18.74 + 18.75 +$(objdir)/ljds7.elf: $(objdir)/dsarm7.o $(objdir)/lookup_tables7.o $(objdir)/dssound7.o 18.76 + $(ARMGCC) -g -mthumb-interwork -mno-fpu -specs=ds_arm7.specs $^ -L$(NDSLIB_LIB) -lnds7 -o $@ 18.77 + 18.78 +$(objdir)/%7.o: $(srcdir)/%7.c 18.79 + $(ARMGCC) $(CFLAGS7) -I$(NDSLIB_INCLUDE) -MMD -DARM7 -c $< -o $@ 18.80 + @cp $(objdir)/$*.d $(objdir)/$*.P; \ 18.81 + sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ 18.82 + -e '/^$$/ d' -e 's/$$/ :/' < $(objdir)/$*.d >> $(objdir)/$*.P; \ 18.83 + rm -f $(objdir)/$*.d 18.84 + 18.85 +$(objdir)/%.o: $(srcdir)/%.c $(srcdir)/ljds.h 18.86 + $(ARMGCC) $(CFLAGS9) -I$(NDSLIB_INCLUDE) -MMD -DARM9 -c $< -o $@ 18.87 + @cp $(objdir)/$*.d $(objdir)/$*.P; \ 18.88 + sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ 18.89 + -e '/^$$/ d' -e 's/$$/ :/' < $(objdir)/$*.d >> $(objdir)/$*.P; \ 18.90 + rm -f $(objdir)/$*.d 18.91 + 18.92 +%.o: %.s 18.93 + $(ARMGCC) $(CFLAGS9) -I$(NDSLIB_INCLUDE) -MMD -DARM9 -c $< -o $@ 18.94 + @cp $(objdir)/$*.d $(objdir)/$*.P; \ 18.95 + sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ 18.96 + -e '/^$$/ d' -e 's/$$/ :/' < $(objdir)/$*.d >> $(objdir)/$*.P; \ 18.97 + rm -f $(objdir)/$*.d 18.98 + 18.99 +$(ASSETSFILE): $(ASSETS) 18.100 + bin2s $^ > $@ 18.101 + 18.102 +# Handle header dependencies 18.103 + 18.104 +-include $(DEPOBJS:%.o=%.P) 18.105 +# The master copy of the variable width font library is in another folder 18.106 + 18.107 +src/fontdraw%: /e/games/ac/double/rac/src/fontdraw% 18.108 + cp $< $@ 18.109 + 18.110 +#ejector is at http://jimprice.com/jim-soft.shtml#eject 18.111 +#and is not necessary for building the program 18.112 +cf: lj.nds 18.113 + cp lj.nds /h/ 18.114 + ejector H: 18.115 + 18.116 +# PC side rules 18.117 + 18.118 +tools/fontconv.exe: tools/fontconv.o 18.119 + gcc -Wall -s $^ -lalleg -o $@ 18.120 + 18.121 +tools/mktables.exe: tools/mktables.o 18.122 + gcc -Wall -s $^ -o $@ 18.123 + 18.124 +tools/%.o: tools/%.c 18.125 + gcc -Wall -O2 -std=gnu99 -c $< -o $@ 18.126 + 18.127 +$(objdir)/vwfont.bin: tools/fontconv.exe $(srcdir)/font.bmp 18.128 + $^ $@ 18.129 + 18.130 +$(objdir)/lookup_tables7.s: tools/mktables.exe 18.131 + $< 18.132 + 18.133 +# Cleanup rules 18.134 + 18.135 +clean: 18.136 + -rm $(objdir)/*.s 18.137 + -rm $(objdir)/*.o 18.138 + -rm $(objdir)/*.P 18.139 +
19.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 19.2 +++ b/gbamakefile Fri Mar 13 00:39:12 2009 -0700 19.3 @@ -0,0 +1,135 @@ 19.4 +# Makefile for Game Boy Advance version of LOCKJAW 19.5 +# 19.6 +# Copr. 2006-2007 Damian Yerrick 19.7 +# 19.8 +# This work is free software; you can redistribute it and/or modify 19.9 +# it under the terms of the GNU General Public License as published by 19.10 +# the Free Software Foundation; either version 2 of the License, or 19.11 +# (at your option) any later version. 19.12 +# 19.13 +# This program is distributed in the hope that it will be useful, 19.14 +# but WITHOUT ANY WARRANTY; without even the implied warranty of 19.15 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19.16 +# GNU General Public License for more details. 19.17 +# 19.18 +# You should have received a copy of the GNU General Public License 19.19 +# along with this program; if not, write to the Free Software 19.20 +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19.21 + 19.22 + 19.23 +srcdir := src 19.24 +objdir := obj/gba 19.25 +EXE := $(objdir)/lj.elf 19.26 +ASSETSFILE := $(objdir)/assets.s 19.27 +INCGBA := -I$(DEVKITPRO)/libgba/include 19.28 +LIBGBA := -L$(DEVKITPRO)/libgba/lib -lgba 19.29 +THUMB_CFLAGS := -mthumb -Wall -O2 -std=gnu99 -mcpu=arm7tdmi \ 19.30 +-DFONTDRAW_SPLIT_COMPILE -DNO_DATETIME -DDEBRIEF_SHORT 19.31 +ARM_CFLAGS := -marm -Wall -O2 -std=gnu99 -mcpu=arm7tdmi \ 19.32 +-DFONTDRAW_SPLIT_COMPILE 19.33 +CC := arm-eabi-gcc -mthumb-interwork 19.34 +LD := arm-eabi-gcc -mthumb-interwork 19.35 +LDFLAGS := -mthumb -Wall -specs=gba_mb.specs 19.36 + 19.37 +MUSICOBJS := $(objdir)/gbasound.o $(objdir)/gbanotefreq.o 19.38 +MUSICLIBS := 19.39 + 19.40 +# The wintermute-approved way to ensure devkitARM is in the PATH 19.41 +ifeq ($(strip $(DEVKITPRO)),) 19.42 +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>devkitPro) 19.43 +endif 19.44 +ifeq ($(strip $(DEVKITARM)),) 19.45 +$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM) 19.46 +endif 19.47 +export PATH := $(DEVKITARM)/bin:$(PATH) 19.48 + 19.49 +# Objects 19.50 + 19.51 +LDLIBS := $(LIBGBA) 19.52 +DEPOBJS := \ 19.53 +$(objdir)/ljgba.o $(objdir)/gbaopt.o $(objdir)/gbaisr.iwram.o \ 19.54 +$(objdir)/ljplay.o $(objdir)/lj.o $(objdir)/speed.o $(objdir)/gimmicks.o \ 19.55 +$(objdir)/options.o $(objdir)/wktables.o $(objdir)/fontdraw.o \ 19.56 +$(objdir)/fontdraw.iwram.o $(objdir)/macro.o $(objdir)/random.o \ 19.57 +$(objdir)/gba_asm.o $(objdir)/ljlocale.o $(objdir)/debrief.o \ 19.58 +$(objdir)/dsdebrief.o $(objdir)/gbamenus.o $(objdir)/dsjoy.o \ 19.59 +$(MUSICOBJS) 19.60 +ASSETS := $(srcdir)/text.chr $(srcdir)/gbablk.chr $(objdir)/vwfont.bin 19.61 +#deleted: ljpc pcjoy options debrief ljreplay ljmusic 19.62 + 19.63 +OTHEROBJS := $(ASSETSFILE) 19.64 + 19.65 +run: lj.gba 19.66 + start $< 19.67 + 19.68 +#ejector is at http://jimprice.com/jim-soft.shtml#eject 19.69 +#and is not necessary for building the program 19.70 +cf: lj.gba 19.71 + cp lj.gba /h/gba/ 19.72 + ejector H: 19.73 + 19.74 +$(EXE): $(DEPOBJS) $(OTHEROBJS) 19.75 + $(LD) $(LDFLAGS) $^ $(MUSICLIBS) $(LDLIBS) -o $@ 19.76 + 19.77 +$(ASSETSFILE): $(ASSETS) 19.78 + bin2s $^ > $@ 19.79 + 19.80 +# Compilation rules 19.81 + 19.82 +%.gba: $(objdir)/%.elf 19.83 + arm-eabi-objcopy -O binary $< $@ 19.84 + gbafix -tLOCKJAW $@ 19.85 + 19.86 +$(objdir)/%.iwram.o: $(srcdir)/%.iwram.c 19.87 + $(CC) $(ARM_CFLAGS) -MMD -c -o $@ $< $(INCGBA) 19.88 + @cp $(objdir)/$*.iwram.d $(objdir)/$*.iwram.P; \ 19.89 + sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ 19.90 + -e '/^$$/ d' -e 's/$$/ :/' < $(objdir)/$*.iwram.d >> $(objdir)/$*.iwram.P; \ 19.91 + rm -f $(objdir)/$*.iwram.d 19.92 + 19.93 +$(objdir)/%.o: $(srcdir)/%.c 19.94 + $(CC) $(THUMB_CFLAGS) -MMD -c -o $@ $< $(INCGBA) 19.95 + @cp $(objdir)/$*.d $(objdir)/$*.P; \ 19.96 + sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ 19.97 + -e '/^$$/ d' -e 's/$$/ :/' < $(objdir)/$*.d >> $(objdir)/$*.P; \ 19.98 + rm -f $(objdir)/$*.d 19.99 + 19.100 +$(objdir)/%.o: $(srcdir)/%.s 19.101 + $(CC) -MMD -c -o $@ $< $(INCGBA) 19.102 + @cp $(objdir)/$*.d $(objdir)/$*.P; \ 19.103 + sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ 19.104 + -e '/^$$/ d' -e 's/$$/ :/' < $(objdir)/$*.d >> $(objdir)/$*.P; \ 19.105 + rm -f $(objdir)/$*.d 19.106 + 19.107 +$(objdir)/%.o: $(objdir)/%.s 19.108 + $(CC) -c -o $@ $< 19.109 + 19.110 +# The master copy of the variable width font library is in another folder 19.111 + 19.112 +src/fontdraw%: /e/games/ac/double/rac/src/fontdraw% 19.113 + cp $< $@ 19.114 + 19.115 +src/font.bmp: /e/games/ac/double/rac/src/font.bmp 19.116 + cp $< $@ 19.117 + 19.118 +$(objdir)/vwfont.bin: tools/fontconv.exe $(srcdir)/font.bmp 19.119 + $^ $@ 19.120 + 19.121 +tools/fontconv.exe: tools/fontconv.o 19.122 + gcc -Wall -s $^ -lalleg -o $@ 19.123 + 19.124 +tools/%.o: tools/%.c 19.125 + gcc -Wall -O2 -std=gnu99 -c $< -o $@ 19.126 + 19.127 +# Header dependencies 19.128 + 19.129 +-include $(DEPOBJS:%.o=%.P) 19.130 + 19.131 + 19.132 +# Cleanup rules 19.133 +.PHONY: clean run cf 19.134 + 19.135 +clean: 19.136 + -rm $(objdir)/*.s 19.137 + -rm $(objdir)/*.o 19.138 + -rm $(objdir)/*.P
20.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 20.2 +++ b/makefile Fri Mar 13 00:39:12 2009 -0700 20.3 @@ -0,0 +1,108 @@ 20.4 +# Makefile for Allegro version of LOCKJAW 20.5 +# under Windows and Linux 20.6 +# 20.7 +# Copr. 2006-2007 Damian Yerrick 20.8 +# 20.9 +# This work is free software; you can redistribute it and/or modify 20.10 +# it under the terms of the GNU General Public License as published by 20.11 +# the Free Software Foundation; either version 2 of the License, or 20.12 +# (at your option) any later version. 20.13 +# 20.14 +# This program is distributed in the hope that it will be useful, 20.15 +# but WITHOUT ANY WARRANTY; without even the implied warranty of 20.16 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20.17 +# GNU General Public License for more details. 20.18 +# 20.19 +# You should have received a copy of the GNU General Public License 20.20 +# along with this program; if not, write to the Free Software 20.21 +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20.22 + 20.23 + 20.24 +EXE := lj.exe 20.25 +CFLAGS := -Wall -O2 -std=gnu99 -DWITH_REPLAY=1 -DHAS_FPU 20.26 +CC := gcc 20.27 +LD := gcc 20.28 +LDFLAGS := -Wall -s -mwindows 20.29 +srcdir := src 20.30 +objdir := obj/win32 20.31 + 20.32 +# Set this to the drive letter of your CF or SD card adapter 20.33 +# so that 'make cf' works 20.34 +# for info about Ejector by Jim Price, see the GBA makefile 20.35 +CFDRIVE := h 20.36 + 20.37 +MUSICOBJS := $(objdir)/ljvorbis.o 20.38 +MUSICLIBS := -laldmb -ldumb -lvorbisfile -lvorbis -logg 20.39 + 20.40 +LDLIBS := -lshfolder -ljpgal -lalleg 20.41 +LINUXLDLIBS := -ljpgal `allegro-config --libs` 20.42 +DEPOBJS := $(objdir)/ljpc.o $(objdir)/lj.o $(objdir)/ljplay.o \ 20.43 +$(objdir)/pcjoy.o $(objdir)/gimmicks.o $(objdir)/random.o \ 20.44 +$(objdir)/wktables.o $(objdir)/speed.o $(objdir)/options.o \ 20.45 +$(objdir)/debrief.o $(objdir)/macro.o $(objdir)/ljreplay.o \ 20.46 +$(objdir)/ljmusic.o $(objdir)/old_pc_options.o $(objdir)/pcsound.o \ 20.47 +$(objdir)/pcdebrief.o $(objdir)/ljlocale.o $(objdir)/scenario.o \ 20.48 +$(objdir)/ljpath.o \ 20.49 +$(MUSICOBJS) 20.50 + 20.51 +OTHEROBJS := $(objdir)/winicon.o 20.52 + 20.53 +.PHONY: pc clean lj.gba lj.nds cf all run 20.54 + 20.55 +# Scripts to coordinate building the PC, GBA, and DS versions 20.56 + 20.57 +pc: $(EXE) 20.58 + 20.59 +run: $(EXE) 20.60 + ./lj 20.61 + 20.62 +all: $(EXE) lj.gba lj.nds 20.63 + 20.64 +cf: /$(CFDRIVE)/gba/lj.gba /$(CFDRIVE)/lj.nds 20.65 + #ejector from http://www.jimprice.com/jim-soft.shtml#eject 20.66 + ejector $(CFDRIVE): 20.67 + 20.68 +/$(CFDRIVE)/gba/lj.gba: lj.gba 20.69 + cp $< $@ 20.70 + 20.71 +/$(CFDRIVE)/lj.nds: lj.nds 20.72 + cp $< $@ 20.73 + 20.74 +lj.gba: 20.75 + +make -f gbamakefile $@ 20.76 + 20.77 +lj.nds: 20.78 + +make -f dsmakefile $@ 20.79 + 20.80 +# Content of executable 20.81 + 20.82 +$(EXE): $(DEPOBJS) $(OTHEROBJS) 20.83 + $(LD) $(LDFLAGS) $^ $(MUSICLIBS) $(LDLIBS) -o $@ 20.84 + 20.85 +# kesiev's makefile for GNU/Linux 20.86 + 20.87 +linux: $(DEPOBJS) 20.88 + $(LD) $(LDFLAGS) $^ $(MUSICLIBS) $(LINUXLDLIBS) -o lj 20.89 + 20.90 +# Compilation rules 20.91 + 20.92 +$(objdir)/%.o: $(srcdir)/%.c 20.93 + $(CC) $(CFLAGS) -MMD -c -o $@ $< 20.94 + @cp $(objdir)/$*.d $(objdir)/$*.P; \ 20.95 + sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \ 20.96 + -e '/^$$/ d' -e 's/$$/ :/' < $(objdir)/$*.d >> $(objdir)/$*.P; \ 20.97 + rm -f $(objdir)/$*.d 20.98 + 20.99 +$(objdir)/%.o: src/%.rc docs/favicon.ico 20.100 + windres -i $< -o $@ 20.101 + 20.102 +# Header dependencies 20.103 + 20.104 +-include $(DEPOBJS:%.o=%.P) 20.105 + 20.106 + 20.107 +# Cleanup rules 20.108 + 20.109 +clean: 20.110 + -rm $(objdir)/*.o 20.111 + -rm $(objdir)/*.P
21.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 21.2 +++ b/mkzip.bat Fri Mar 13 00:39:12 2009 -0700 21.3 @@ -0,0 +1,16 @@ 21.4 +@echo off 21.5 +echo Compressing 21.6 +upx -9 lj.exe 21.7 + 21.8 +echo Sending zip to local copy of Pin Eight 21.9 +zip -9 -u lj-src.zip -@ < zip.in 21.10 + 21.11 +zip -9 -u lj.zip -@ < binzip.in 21.12 + 21.13 +copy lj.zip E:\p8\lj\ 21.14 +copy lj-src.zip E:\p8\lj\ 21.15 +copy lj-contrib.zip E:\p8\lj\ 21.16 +copy README.html E:\p8\lj\ 21.17 +copy docs\ljhtml.css E:\p8\lj\docs\ 21.18 +start E:\p8\lj 21.19 +echo Now FTP this to the server. 21.20 \ No newline at end of file
22.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 22.2 +++ b/obj/ds/index.txt Fri Mar 13 00:39:12 2009 -0700 22.3 @@ -0,0 +1,1 @@ 22.4 +Windows object files go here.
23.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 23.2 +++ b/obj/gba/index.txt Fri Mar 13 00:39:12 2009 -0700 23.3 @@ -0,0 +1,1 @@ 23.4 +Windows object files go here.
24.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 24.2 +++ b/obj/win32/index.txt Fri Mar 13 00:39:12 2009 -0700 24.3 @@ -0,0 +1,1 @@ 24.4 +Windows object files go here.
25.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 25.2 +++ b/src/debrief.c Fri Mar 13 00:39:12 2009 -0700 25.3 @@ -0,0 +1,603 @@ 25.4 +/* PC frontend for LOCKJAW, an implementation of the Soviet Mind Game 25.5 + 25.6 +Copyright (C) 2006 Damian Yerrick <tepples+lj@spamcop.net> 25.7 + 25.8 +This work is free software; you can redistribute it and/or modify 25.9 +it under the terms of the GNU General Public License as published by 25.10 +the Free Software Foundation; either version 2 of the License, or 25.11 +(at your option) any later version. 25.12 + 25.13 +This program is distributed in the hope that it will be useful, 25.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 25.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25.16 +GNU General Public License for more details. 25.17 + 25.18 +You should have received a copy of the GNU General Public License 25.19 +along with this program; if not, write to the Free Software 25.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 25.21 + 25.22 +Original game concept and design by Alexey Pajitnov. 25.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 25.24 +or The Tetris Company LLC. 25.25 + 25.26 +*/ 25.27 +#include "ljplay.h" 25.28 +#include "options.h" 25.29 +#include <stdio.h> 25.30 +#ifndef NO_DATETIME 25.31 +#include <time.h> 25.32 +#endif 25.33 + 25.34 +#define USING_DEBRIEF_PAGE 1 25.35 +#define MIN_RECTUM_ROWS 4 25.36 +#define MIN_ZIGZAG_ROWS 2 25.37 + 25.38 +// On newlib, use a variant of sprintf that doesn't handle floating 25.39 +// point formats. On other C libraries, use standard sprintf. 25.40 +#ifdef HAS_FPU 25.41 +#define siprintf sprintf 25.42 +#ifndef HAS_FOPEN 25.43 +#define HAS_FOPEN 25.44 +#endif 25.45 +#define DEBRIEF_TO_STDOUT 25.46 +#endif 25.47 + 25.48 +#ifdef HAS_FOPEN 25.49 +#include "ljpath.h" // for ljfopen 25.50 +#endif 25.51 + 25.52 +const char *const debriefBoolNames[2] = { 25.53 + "Off", "On" 25.54 +}; 25.55 + 25.56 +typedef struct TimeResultMSF { 25.57 + unsigned int pieces_100m; 25.58 + unsigned int garbage_100m; 25.59 + unsigned int minutes; 25.60 + unsigned char seconds; 25.61 + unsigned char frames; 25.62 +} TimeResultMSF; 25.63 + 25.64 +void frames2msf(unsigned int gameTime, 25.65 + unsigned int nPieces, 25.66 + unsigned int outGarbage, 25.67 + TimeResultMSF *out) { 25.68 + unsigned int gameSeconds = gameTime / 60U; 25.69 + unsigned int gameMinutes = gameSeconds / 60U; 25.70 + out->pieces_100m = gameTime 25.71 + ? 360000ULL * nPieces / gameTime 25.72 + : 0; 25.73 + out->garbage_100m = gameTime 25.74 + ? 360000ULL * outGarbage / gameTime 25.75 + : 0; 25.76 + out->minutes = gameMinutes; 25.77 + out->seconds = gameSeconds - gameMinutes * 60U; 25.78 + out->frames = gameTime - gameSeconds * 60U; 25.79 +} 25.80 + 25.81 +static unsigned int countBlocksLeft(const LJField *p) { 25.82 + unsigned int n = 0; 25.83 + 25.84 + for (int y = 0; y < LJ_PF_HT; ++y) { 25.85 + for (int x = 0; x < LJ_PF_WID; ++x) { 25.86 + if (p->b[y][x]) { 25.87 + ++n; 25.88 + } 25.89 + } 25.90 + } 25.91 + return n; 25.92 +} 25.93 + 25.94 +/** 25.95 + * Calculates the number of rows starting at the bottom that 25.96 + * conform to a zigzag pattern. 25.97 + * @param p the playfield to test 25.98 + * @return the number of rows successfully completed 25.99 + */ 25.100 +unsigned int calcZigzagGrade(const LJField *p) { 25.101 + unsigned int hole = p->leftWall; 25.102 + int delta = 1; 25.103 + 25.104 + for (size_t y = 0; y < p->ceiling; ++y) { 25.105 + 25.106 + // invariant: 25.107 + // at this point, y equals the number of rows known to conform 25.108 + for (size_t x = p->leftWall; x < p->rightWall; ++x) { 25.109 + unsigned int blk = p->b[y][x]; 25.110 + 25.111 + // A block should be in all cells of the row 25.112 + // except for the hole cell, which should be empty. 25.113 + if (x == hole) { 25.114 + 25.115 + // if there's a block where a hole should be, this row 25.116 + // doesn't conform, but all the rows below do conform 25.117 + if (blk) { 25.118 + return y; 25.119 + } 25.120 + } else { 25.121 + 25.122 + // if there's a hole where a block should be, this row 25.123 + // doesn't conform, but all the rows below do conform 25.124 + if (!blk) { 25.125 + return y; 25.126 + } 25.127 + } 25.128 + } 25.129 + 25.130 + // if this hole isn't covered up on the next row, 25.131 + // this row doesn't conform either 25.132 + // changed in 0.43 after a clarification from Kitaru 25.133 + if (!p->b[y + 1][hole]) { 25.134 + return y; 25.135 + } 25.136 + 25.137 + // by now we know that the row conforms, 25.138 + // so move the hole for the next row 25.139 + if (hole == p->rightWall - 1) { 25.140 + delta = -1; 25.141 + } else if (hole == p->leftWall) { 25.142 + delta = 1; 25.143 + } 25.144 + hole += delta; 25.145 + 25.146 + } 25.147 + return p->ceiling; 25.148 +} 25.149 + 25.150 + 25.151 +/** 25.152 + * Calculates the number of rows in a playfield that were prepared 25.153 + * for an I tetromino. 25.154 + * Rule for this pattern: 25.155 + * 1. Only one hole on the bottom row. 25.156 + * 2. This column must be unoccupied below the ceiling. 25.157 + * 3. Conforming rows must be full except for the hole. 25.158 + * 25.159 + * Easy test procedure for this pattern: 25.160 + * Level 4 garbage 25.161 + * @return the number of rows that conform to constraint 3, or 0 if the 25.162 + * field does not conform to constraint 1 or 2. 25.163 + */ 25.164 +unsigned int calcRectumGrade(const LJField *p) { 25.165 + unsigned int hole = LJ_PF_WID; 25.166 + 25.167 + // search for the first hole on the bottom row 25.168 + for (unsigned int x = p->leftWall; 25.169 + x < p->rightWall; 25.170 + ++x) { 25.171 + if (!p->b[0][x]) { 25.172 + hole = x; 25.173 + break; 25.174 + } 25.175 + } 25.176 + 25.177 + // If there is no hole in the bottom row, then 0 rows conform. 25.178 + // This shouldn't happen in standard smg because the line would be 25.179 + // cleared, but eventually LJ will support games other than SMG, 25.180 + // and checking the array bounds is O(1), so why not? 25.181 + if (hole >= p->rightWall) { 25.182 + return 0; 25.183 + } 25.184 + 25.185 + // make sure that the row is clear through the whole visible 25.186 + // portion of the playfield 25.187 + for (unsigned int y = 0; y < p->ceiling; ++y) { 25.188 + 25.189 + // If this column isn't empty, the whole field doesn't conform. 25.190 + if (p->b[y][hole]) { 25.191 + return 0; 25.192 + } 25.193 + } 25.194 + 25.195 + for (unsigned int y = 0; y < p->ceiling; ++y) { 25.196 + 25.197 + // At this point, the bottom y rows conform. 25.198 + for (unsigned int x = p->leftWall; x < p->rightWall; ++x) { 25.199 + 25.200 + // Disregarding the hole column, if there's an empty cell 25.201 + // in this row, then this row does not conform. 25.202 + if (x != hole && !p->b[y][x]) { 25.203 + return y; 25.204 + } 25.205 + } 25.206 + } 25.207 + return p->ceiling; 25.208 +} 25.209 + 25.210 +/** 25.211 + * Converts a rank number (0-19) to a text rank. 25.212 + * 0-9: 10-1 kyu; 10-99: 1-90 dan; 100+: grandmaster 25.213 + * @return number of characters written 25.214 + */ 25.215 +static size_t printSecretGrade(char *dst, int rank) { 25.216 + if (rank >= 100) { 25.217 + return siprintf(dst, "GM"); 25.218 + } else if (rank >= 10) { 25.219 + return siprintf(dst, "S%d", rank - 9); 25.220 + } else { 25.221 + return siprintf(dst, "%d", 10 - rank); 25.222 + } 25.223 +} 25.224 + 25.225 +static size_t printFracG(char *dst, unsigned int value) { 25.226 + if (value == 0) { 25.227 + return siprintf(dst, "Instant"); 25.228 + } else if (value == 1) { 25.229 + return siprintf(dst, "1G"); 25.230 + } else { 25.231 + return siprintf(dst, "1/%dG", value); 25.232 + } 25.233 +} 25.234 + 25.235 +#include <string.h> // remove once fourcc transition is complete 25.236 +static size_t printFourCC(char *dst, FourCC f) { 25.237 + const char *data = ljGetFourCCName(f); 25.238 + if (data) { 25.239 + int pos = 0; 25.240 + for(pos = 0; *data; ++pos) { 25.241 + dst[pos] = *data++; 25.242 + } 25.243 + return pos; 25.244 + } else { 25.245 + int pos = 0; 25.246 + for(pos = 0; f.c[pos] && pos < 4; ++pos) { 25.247 + dst[pos] = f.c[pos]; 25.248 + } 25.249 + return pos; 25.250 + } 25.251 +} 25.252 + 25.253 +size_t buildOptionsReportPage(char *dst, const LJView *v) { 25.254 + size_t pos = 0; 25.255 + 25.256 + 25.257 +#if USING_DEBRIEF_PAGE 25.258 + const LJField *p = v->field; 25.259 + 25.260 + pos += siprintf(dst + pos, 25.261 + "Options\n\n" 25.262 + "Well: %dx%d%s, Enter: %s\n" 25.263 + "Speed: ", 25.264 + p->rightWall - p->leftWall, 25.265 + p->ceiling, 25.266 + v->hidePF ? " invisible" : "", 25.267 + p->enterAbove ? "Above" : "Below"); 25.268 + pos += printFourCC(dst + pos, 25.269 + optionsSpeedCurveNames[(size_t)p->speedState.curve]); 25.270 + pos += siprintf(dst + pos, 25.271 + ", ARE: %d ms\n" 25.272 + "Randomizer: ", 25.273 + v->field->areStyle * 50 / 3); 25.274 + pos += printFourCC(dst + pos, 25.275 + optionsRandNames[(size_t)p->randomizer]); 25.276 + pos += siprintf(dst + pos, " of "); 25.277 + pos += printFourCC(dst + pos, 25.278 + optionsPieceSetNames[(size_t)p->pieceSet]); 25.279 + pos += siprintf(dst + pos, "\nHold: "); 25.280 + pos += printFourCC(dst + pos, 25.281 + optionsHoldStyleNames[(size_t)p->holdStyle]); 25.282 + pos += siprintf(dst + pos, ", Rotation: "); 25.283 + pos += printFourCC(dst + pos, 25.284 + optionsRotNames[(size_t)p->rotationSystem]); 25.285 + if (p->maxUpwardKicks < 20) { 25.286 + pos += siprintf(dst + pos, 25.287 + " %d FK", 25.288 + (int)p->maxUpwardKicks); 25.289 + } 25.290 + if (v->control->initialRotate) { 25.291 + pos += siprintf(dst + pos, "+initial"); 25.292 + } 25.293 + 25.294 + pos += siprintf(dst + pos, 25.295 + "\nLock: "); 25.296 + if (p->setLockDelay >= 128) { 25.297 + pos += siprintf(dst + pos, "Never"); 25.298 + } else { 25.299 + if (p->setLockDelay > 0) { 25.300 + pos += siprintf(dst + pos, 25.301 + "%d ms ", 25.302 + p->setLockDelay * 50 / 3); 25.303 + } 25.304 + pos += printFourCC(dst + pos, 25.305 + optionsLockdownNames[(size_t)p->lockReset]); 25.306 + } 25.307 + pos += siprintf(dst + pos, 25.308 + ", Deep: %s\n", 25.309 + debriefBoolNames[p->bottomBlocks]); 25.310 + 25.311 + pos += siprintf(dst + pos, 25.312 + "Line clear: %d ms ", 25.313 + p->speed.lineDelay * 50 / 3); 25.314 + pos += printFourCC(dst + pos, 25.315 + optionsGravNames[(size_t)p->clearGravity]); 25.316 + pos += siprintf(dst + pos, 25.317 + ", Gluing: "); 25.318 + pos += printFourCC(dst + pos, 25.319 + optionsGluingNames[(size_t)p->gluing]); 25.320 + pos += siprintf(dst + pos, 25.321 + "\nDrop score: "); 25.322 + pos += printFourCC(dst + pos, 25.323 + optionsDropScoringNames[(size_t)p->dropScoreStyle]); 25.324 + pos += siprintf(dst + pos, 25.325 + ", T-spin: "); 25.326 + pos += printFourCC(dst + pos, 25.327 + optionsTspinNames[(size_t)p->tSpinAlgo]); 25.328 + pos += siprintf(dst + pos, 25.329 + "\nGarbage: "); 25.330 + pos += printFourCC(dst + pos, 25.331 + optionsGarbageNames[(size_t)p->garbageStyle]); 25.332 + pos += siprintf(dst + pos, 25.333 + ", DAS: %d ms ", 25.334 + v->control->dasDelay * 50 / 3); 25.335 + pos += printFracG(dst + pos, v->control->dasSpeed); 25.336 + pos += siprintf(dst + pos, 25.337 + "\nSoft drop: "); 25.338 + pos += printFracG(dst + pos, v->control->softDropSpeed + 1); 25.339 + dst[pos++] = ' '; 25.340 + pos += printFourCC(dst + pos, 25.341 + optionsZangiNames[v->control->softDropLock]); 25.342 + pos += siprintf(dst + pos, 25.343 + ", Hard drop: "); 25.344 + pos += printFourCC(dst + pos, 25.345 + optionsZangiNames[v->control->hardDropLock]); 25.346 + pos += siprintf(dst + pos, "\nShadow: "); 25.347 + pos += printFourCC(dst + pos, 25.348 + optionsShadowNames[v->hideShadow]); 25.349 + pos += siprintf(dst + pos, 25.350 + ", Next: %d\n", 25.351 + v->nextPieces); 25.352 +#else 25.353 + pos += siprintf(dst, "\n\nDebrief disabled.\n"); 25.354 +#endif 25.355 + 25.356 + dst[pos] = 0; 25.357 + return pos; 25.358 +} 25.359 + 25.360 +#ifdef DEBRIEF_SHORT 25.361 +static const char *const naiveGravity[8] = { 25.362 + "1L", "2L", "3L", "4L", 25.363 + "T1", "T2", "T3" 25.364 +}; 25.365 +static const char *const cascadeGravity[8] = { 25.366 + "1L", "2L", "3L", "4L", 25.367 + "5L", "6L", "7L", "8L+" 25.368 +}; 25.369 +#else 25.370 +static const char *const naiveGravity[8] = { 25.371 + "single", "double", "triple", "home run", 25.372 + "T single", "T double", "T triple" 25.373 +}; 25.374 +static const char *const cascadeGravity[8] = { 25.375 + "single", "double", "triple", "quad", 25.376 + "5L", "6L", "7L", "8L+" 25.377 +}; 25.378 +#endif 25.379 + 25.380 +static size_t printLineCounts(char *dst, const LJField *p) { 25.381 + size_t pos = 0; 25.382 + const char *const *names = (p->clearGravity == LJGRAV_NAIVE) 25.383 + ? naiveGravity : cascadeGravity; 25.384 + dst[pos++] = '('; 25.385 + for (int i = 0; i < 8; ++i) { 25.386 + if (names[i]) { 25.387 + pos += siprintf(dst + pos, 25.388 + "%s%s: %u", 25.389 + i ? "; " : "", 25.390 + names[i], 25.391 + p->nLineClears[i]); 25.392 + } 25.393 + } 25.394 + dst[pos++] = ')'; 25.395 + dst[pos++] = '\n'; 25.396 + return pos; 25.397 +} 25.398 + 25.399 + 25.400 +size_t buildDebriefPage(char *dst, const LJView *v) { 25.401 + size_t pos = 0; 25.402 + 25.403 +#if USING_DEBRIEF_PAGE 25.404 +#ifndef NO_DATETIME 25.405 + const time_t finishTimeUNIX = time(NULL); 25.406 + const struct tm *finishTime = localtime(&finishTimeUNIX); 25.407 + char finishTimeStr[64]; 25.408 + 25.409 + /* I would have used 25.410 + strftime(finishTimeStr, sizeof(finishTimeStr), "%Y-%m-%d at %H:%M", finishTime); 25.411 + but it brings in the floating-point library on GBA/DS. */ 25.412 + siprintf(finishTimeStr, 25.413 + "%04d-%02d-%02d at %02d:%02d", 25.414 + finishTime->tm_year + 1900, 25.415 + finishTime->tm_mon + 1, finishTime->tm_mday, 25.416 + finishTime->tm_hour, finishTime->tm_min); 25.417 +#endif 25.418 + 25.419 + const LJField *p = v->field; 25.420 + unsigned long int keys_100m = p->nPieces 25.421 + ? 100U * v->control->presses / p->nPieces 25.422 + : 666; 25.423 + TimeResultMSF gameMSF, activeMSF; 25.424 + const char *wordPieces = (p->nPieces != 1) ? "tetrominoes" : "tetromino"; 25.425 + if (p->pieceSet == LJRAND_234BLK) { 25.426 + wordPieces = (p->nPieces != 1) ? "pieces" : "piece"; 25.427 + } 25.428 + 25.429 + /* Secret grades */ 25.430 + unsigned int nBlocksLeft = countBlocksLeft(p); 25.431 + unsigned int nZigzagRows = calcZigzagGrade(p); 25.432 + unsigned int nRectumRows = calcRectumGrade(p); 25.433 + 25.434 + pos += siprintf(dst + pos, 25.435 + "Result:\n\n%s ", 25.436 + v->control->countdown <= 0 ? "Cleared" : "Stopped"); 25.437 + pos += printFourCC(dst + pos, gimmickNames[p->gimmick]); 25.438 + pos += siprintf(dst + pos, 25.439 + " at level %d\n", 25.440 + p->speedState.level); 25.441 +#if !defined(NO_DATETIME) || defined(WITH_REPLAY) 25.442 +#ifndef NO_DATETIME 25.443 + pos += siprintf(dst + pos, "on %s", finishTimeStr); 25.444 +#endif 25.445 +#ifdef WITH_REPLAY 25.446 + if (p->reloaded) { 25.447 + pos += siprintf(dst + pos, " from saved state"); 25.448 + } 25.449 +#endif 25.450 + dst[pos++] = '\n'; 25.451 +#endif 25.452 + 25.453 + frames2msf(p->gameTime, p->nPieces, p->outGarbage, &gameMSF); 25.454 + frames2msf(p->activeTime, p->nPieces, p->outGarbage, &activeMSF); 25.455 + 25.456 + pos += siprintf(dst + pos, 25.457 + "Played %d %s in %u:%02u.%02u (%u.%02u/min)\n", 25.458 + p->nPieces, 25.459 + wordPieces, 25.460 + gameMSF.minutes, 25.461 + (unsigned int)gameMSF.seconds, 25.462 + gameMSF.frames * 5U / 3U, 25.463 + (unsigned int) (gameMSF.pieces_100m / 100), 25.464 + (unsigned int) (gameMSF.pieces_100m % 100)); 25.465 + pos += siprintf(dst + pos, 25.466 + "(active time only: %u:%02u.%02u, %u.%02u/min)\n", 25.467 + activeMSF.minutes, 25.468 + (unsigned int)activeMSF.seconds, 25.469 + activeMSF.frames * 5U / 3U, 25.470 + (unsigned int) (activeMSF.pieces_100m / 100), 25.471 + (unsigned int) (activeMSF.pieces_100m % 100)); 25.472 + pos += siprintf(dst + pos, 25.473 + "Pressed %u keys (%u.%02u/piece)\n\n", 25.474 + v->control->presses, 25.475 + (unsigned int) (keys_100m / 100), 25.476 + (unsigned int) (keys_100m % 100)); 25.477 + 25.478 + pos += siprintf(dst + pos, 25.479 + "Made %u lines", 25.480 + p->lines); 25.481 + if (p->gluing == LJGLUING_SQUARE) { 25.482 + pos += siprintf(dst + pos, 25.483 + " and %u pure + %u squares", 25.484 + p->monosquares, p->multisquares); 25.485 + } 25.486 + dst[pos++] = '\n'; 25.487 + pos += printLineCounts(dst + pos, p); 25.488 + pos += siprintf(dst + pos, 25.489 + "Sent %u garbage (%u.%02u per minute)\n", 25.490 + p->outGarbage, 25.491 + (unsigned int) (gameMSF.garbage_100m / 100), 25.492 + (unsigned int) (gameMSF.garbage_100m % 100)); 25.493 + if (nZigzagRows >= MIN_ZIGZAG_ROWS) { 25.494 + pos += siprintf(dst + pos, 25.495 + "Made %u rows of > for grade ", 25.496 + nZigzagRows); 25.497 + pos += printSecretGrade(dst + pos, 25.498 + nZigzagRows >= 19 ? 100 : nZigzagRows); 25.499 + } else if (nRectumRows >= MIN_RECTUM_ROWS) { 25.500 + pos += siprintf(dst + pos, 25.501 + "Made %u-row rectum for grade ", 25.502 + nRectumRows); 25.503 + pos += printSecretGrade(dst + pos, 25.504 + nRectumRows >= 20 ? 100 : nRectumRows - 1); 25.505 + } else if (nBlocksLeft >= 110 && nBlocksLeft <= 111) { 25.506 + pos += siprintf(dst + pos, 25.507 + "Secret grade: Eleventy%s", 25.508 + (nBlocksLeft & 1) ? "-one" : ""); 25.509 + } else { 25.510 + pos += siprintf(dst + pos, 25.511 + "Left %d blocks behind", 25.512 + nBlocksLeft); 25.513 + } 25.514 + dst[pos++] = '\n'; 25.515 + dst[pos++] = '\n'; 25.516 + pos += printFourCC(dst + pos, optionsScoringNames[p->scoreStyle]); 25.517 + 25.518 + pos += siprintf(dst + pos, 25.519 + " score: %d\n", 25.520 + p->score); 25.521 + 25.522 + /* 25.523 + tod stats not in lj 25.524 + 25.525 + points/line, 40 lines score, silver squares, gold squares 25.526 + 25.527 + */ 25.528 + 25.529 +#else 25.530 + pos += siprintf(dst, "\n\nDebrief disabled. Score: %u\n", v->field->score); 25.531 +#endif 25.532 + return pos; 25.533 +} 25.534 + 25.535 +void debriefDrawPage(const char *page, size_t pageNumber); 25.536 +LJBits debriefHandleKeys(void); 25.537 +extern volatile char redrawWholeScreen; 25.538 + 25.539 + 25.540 +/** 25.541 + * Reports the player's performance. 25.542 + */ 25.543 +void debrief(LJView *v) { 25.544 + char pageData[2000]; // current length is under 1000 chars 25.545 + char *page[2] = {pageData, "Page coming soon!"}; 25.546 + int curPage = 0; 25.547 + 25.548 + { 25.549 + size_t pos; 25.550 + 25.551 + pos = buildDebriefPage(page[0], v); 25.552 + page[1] = page[0] + (++pos); 25.553 + pos += buildOptionsReportPage(page[1], v); 25.554 + } 25.555 +#ifdef HAS_FOPEN 25.556 + FILE *logFile = ljfopen("lj-scores.txt", "at"); 25.557 + if (logFile) { 25.558 + fputs("\n\n\n", logFile); 25.559 + fputs(page[0], logFile); 25.560 + fputs(page[1], logFile); 25.561 +#ifdef DEBRIEF_TO_STDOUT 25.562 + fputs(page[0], stdout); 25.563 + fputs(page[1], stdout); 25.564 +#endif 25.565 + fclose(logFile); 25.566 + } 25.567 +#endif 25.568 + 25.569 + LJBits lastKeys = ~0; 25.570 + redrawWholeScreen = 1; 25.571 + debriefHandleKeys(); // call once to clear key buffer 25.572 + 25.573 + for (;;) { 25.574 + LJBits sounds = 0; 25.575 + 25.576 + if (redrawWholeScreen) { 25.577 + redrawWholeScreen = 0; 25.578 + debriefDrawPage(page[curPage], curPage); 25.579 + } 25.580 + LJBits keys = debriefHandleKeys(); 25.581 + LJBits newKeys = keys & ~lastKeys; 25.582 + 25.583 + if (newKeys & VKEY_LEFT) { 25.584 + if (curPage > 0) { 25.585 + curPage -= 1; 25.586 + redrawWholeScreen = 1; 25.587 + sounds |= LJSND_HOLD; 25.588 + } 25.589 + } 25.590 + 25.591 + if (newKeys & VKEY_RIGHT) { 25.592 + if (curPage + 1 < sizeof(page) / sizeof(page[0])) { 25.593 + curPage += 1; 25.594 + redrawWholeScreen = 1; 25.595 + sounds |= LJSND_HOLD; 25.596 + } 25.597 + } 25.598 + 25.599 + if (newKeys & (VKEY_ROTR | VKEY_ROTL)) { 25.600 + break; 25.601 + } else { 25.602 + lastKeys = keys; 25.603 + } 25.604 + playSoundEffects(v, sounds, 100); 25.605 + } 25.606 +}
26.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 26.2 +++ b/src/dsarm7.c Fri Mar 13 00:39:12 2009 -0700 26.3 @@ -0,0 +1,200 @@ 26.4 +/*--------------------------------------------------------------------------------- 26.5 + 26.6 + LOCKJAW Tetromino Game: ARM7 core 26.7 + 26.8 + Copyright (C) 2005-2007 26.9 + Damian Yerrick (tepples) 26.10 + 26.11 + based on 26.12 + default ARM7 core 26.13 + 26.14 + Copyright (C) 2005-2007 26.15 + Michael Noland (joat) 26.16 + Jason Rogers (dovoto) 26.17 + Dave Murphy (WinterMute) 26.18 + 26.19 + This software is provided 'as-is', without any express or implied 26.20 + warranty. In no event will the authors be held liable for any 26.21 + damages arising from the use of this software. 26.22 + 26.23 + Permission is granted to anyone to use this software for any 26.24 + purpose, including commercial applications, and to alter it and 26.25 + redistribute it freely, subject to the following restrictions: 26.26 + 26.27 + 1. The origin of this software must not be misrepresented; you 26.28 + must not claim that you wrote the original software. If you use 26.29 + this software in a product, an acknowledgment in the product 26.30 + documentation would be appreciated but is not required. 26.31 + 2. Altered source versions must be plainly marked as such, and 26.32 + must not be misrepresented as being the original software. 26.33 + 3. This notice may not be removed or altered from any source 26.34 + distribution. 26.35 + 26.36 +---------------------------------------------------------------------------------*/ 26.37 +#include <stdlib.h> 26.38 +#include <nds.h> 26.39 +#include "talkback.h" // custom talkback 26.40 + 26.41 +void ljSoundEffects(unsigned long int sounds, 26.42 + int countdown); 26.43 +void ljMusic(void); 26.44 +extern int order; 26.45 +extern char ticksLeft; 26.46 +extern char row; 26.47 +extern char musicPaused; 26.48 + 26.49 + 26.50 + 26.51 + 26.52 +void powerOffDS(void) 26.53 +{ 26.54 + writePowerManagement(PM_CONTROL_REG, 1 << 6);//6 DS power (0: on, 1: shut down!) 26.55 +} 26.56 + 26.57 +void doSleep(void) { 26.58 + 26.59 + // Check if the lid has been closed. 26.60 + if(IPC->buttons & BIT(7)) { 26.61 + // Save the current interrupt sate. 26.62 + u32 ie_save = REG_IE; 26.63 + // Turn the speaker down. 26.64 + swiChangeSoundBias(0,0x400); 26.65 + // Save current power state. 26.66 + int power = readPowerManagement(PM_CONTROL_REG); 26.67 + // Set sleep LED. 26.68 + writePowerManagement(PM_CONTROL_REG, PM_LED_CONTROL(1)); 26.69 + // Register for the lid interrupt. 26.70 + REG_IE = IRQ_LID; 26.71 + 26.72 + // Power down till we get our interrupt. 26.73 + swiSleep(); //waits for PM (lid open) interrupt 26.74 + 26.75 + REG_IF = ~0; 26.76 + // Restore the interrupt state. 26.77 + REG_IE = ie_save; 26.78 + // Restore power state. 26.79 + writePowerManagement(PM_CONTROL_REG, power); 26.80 + 26.81 + // Turn the speaker up. 26.82 + swiChangeSoundBias(1,0x400); 26.83 + IPC->buttons &= ~BIT(7); 26.84 + } 26.85 +} 26.86 + 26.87 +void VblankHandler(void) { 26.88 + 26.89 + doSleep(); 26.90 + 26.91 + volatile P8A7Talkback *tb = (P8A7Talkback *)IPC->soundData; 26.92 + if (tb) { 26.93 + int cmd = tb->cmd; 26.94 + tb->cmd = 0; 26.95 + 26.96 + switch (cmd) { 26.97 + case TALKBACK_POWER_OFF: 26.98 + powerOFF(POWER_SOUND); 26.99 + powerOffDS(); 26.100 + swiWaitForVBlank(); 26.101 + break; 26.102 + case TALKBACK_PLAY_MUSIC: 26.103 + musicPaused = 0; 26.104 + break; 26.105 + case TALKBACK_STOP_MUSIC: 26.106 + musicPaused = 1; 26.107 + order = 0; 26.108 + row = 0; 26.109 + ticksLeft = 1; 26.110 + break; 26.111 + case TALKBACK_PAUSE_MUSIC: 26.112 + musicPaused = 1; 26.113 + } 26.114 + unsigned long int fx = tb->sounds; 26.115 + tb->sounds = 0; 26.116 + 26.117 + ljSoundEffects(fx, tb->countdown); 26.118 + } 26.119 + 26.120 + ljMusic(); 26.121 + 26.122 +} 26.123 + 26.124 + 26.125 + 26.126 +// All below this is wintermute's and may need to be replaced 26.127 +// with new versions of libnds 26.128 + 26.129 + 26.130 +touchPosition first,tempPos; 26.131 + 26.132 +//--------------------------------------------------------------------------------- 26.133 +void VcountHandler() { 26.134 +//--------------------------------------------------------------------------------- 26.135 + static int lastbut = -1; 26.136 + 26.137 + uint16 but=0, x=0, y=0, xpx=0, ypx=0, z1=0, z2=0; 26.138 + 26.139 + but = REG_KEYXY; 26.140 + 26.141 + if (!( (but ^ lastbut) & (1<<6))) { 26.142 + 26.143 + tempPos = touchReadXY(); 26.144 + 26.145 + if ( tempPos.x == 0 || tempPos.y == 0 ) { 26.146 + but |= (1 <<6); 26.147 + lastbut = but; 26.148 + } else { 26.149 + x = tempPos.x; 26.150 + y = tempPos.y; 26.151 + xpx = tempPos.px; 26.152 + ypx = tempPos.py; 26.153 + z1 = tempPos.z1; 26.154 + z2 = tempPos.z2; 26.155 + } 26.156 + 26.157 + } else { 26.158 + lastbut = but; 26.159 + but |= (1 <<6); 26.160 + } 26.161 + 26.162 + IPC->touchX = x; 26.163 + IPC->touchY = y; 26.164 + IPC->touchXpx = xpx; 26.165 + IPC->touchYpx = ypx; 26.166 + IPC->touchZ1 = z1; 26.167 + IPC->touchZ2 = z2; 26.168 + IPC->buttons = but; 26.169 + 26.170 +} 26.171 + 26.172 + 26.173 + 26.174 +//--------------------------------------------------------------------------------- 26.175 +int main(int argc, char ** argv) { 26.176 +//--------------------------------------------------------------------------------- 26.177 + 26.178 + // set up custom ipc struct 26.179 + IPC->soundData = 0; 26.180 + 26.181 + // read User Settings from firmware 26.182 + readUserSettings(); 26.183 + 26.184 + //enable sound 26.185 + powerON(POWER_SOUND); 26.186 + writePowerManagement(PM_CONTROL_REG, ( readPowerManagement(PM_CONTROL_REG) & ~PM_SOUND_MUTE & ~(1 << 6) ) | PM_SOUND_AMP ); 26.187 + SOUND_CR = SOUND_ENABLE | SOUND_VOL(0x7F); 26.188 + 26.189 + irqInit(); 26.190 + 26.191 + // Start the RTC tracking IRQ 26.192 + initClockIRQ(); 26.193 + 26.194 + SetYtrigger(80); 26.195 + irqSet(IRQ_VCOUNT, VcountHandler); 26.196 + irqSet(IRQ_VBLANK, VblankHandler); 26.197 + 26.198 + 26.199 + irqEnable( IRQ_VBLANK | IRQ_VCOUNT); 26.200 + 26.201 + // Keep the ARM7 mostly idle 26.202 + while (1) swiWaitForVBlank(); 26.203 +}
27.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 27.2 +++ b/src/dsdebrief.c Fri Mar 13 00:39:12 2009 -0700 27.3 @@ -0,0 +1,77 @@ 27.4 +/* 27.5 +GBA/DS debrief code for LOCKJAW Tetromino Game 27.6 + 27.7 +Copr. 2008 Damian Yerrick 27.8 +also GPL 27.9 + 27.10 +*/ 27.11 +#include <stdio.h> 27.12 +#ifdef ARM9 27.13 +#include "ljds.h" 27.14 +#define vwfOptions vwfTouch 27.15 +#else 27.16 +#include "ljgba.h" 27.17 +#define vwfOptions vwfTop 27.18 +#endif 27.19 +#include "fontdraw.h" 27.20 + 27.21 +void debriefDrawPage(const char *page, size_t pageNumber) { 27.22 + int y = 0; 27.23 + char line[256]; 27.24 + int linePos = 0; 27.25 + int done = 0; 27.26 + int scrW = vwfOptions.width * 8; 27.27 + int scrH = vwfOptions.height * 8; 27.28 + 27.29 +#ifdef ARM9 27.30 + BG_PALETTE_SUB[0] = RGB5(31,31,31); 27.31 + BG_PALETTE_SUB[1] = RGB5(21,21,21); 27.32 + BG_PALETTE_SUB[2] = RGB5(0, 0, 0); 27.33 + BG_PALETTE_SUB[3] = RGB5(0, 0, 0); 27.34 + videoSetModeSub(MODE_0_2D 27.35 + | DISPLAY_BG0_ACTIVE); 27.36 +#else 27.37 + BG_PALETTE[0] = RGB5(31,31,31); 27.38 + BG_PALETTE[1] = RGB5(21,21,21); 27.39 + BG_PALETTE[2] = RGB5(0, 0, 0); 27.40 + BG_PALETTE[3] = RGB5(0, 0, 0); 27.41 + REG_DISPCNT = 0 | BG0_ON; 27.42 +#endif 27.43 + vwfWinInit(&vwfOptions); 27.44 + vwfPuts(&vwfOptions, "GAME OVER", (scrW - 64) / 2, y); 27.45 + siprintf(line, "Page %u", (unsigned int)pageNumber + 1); 27.46 + vwfPuts(&vwfOptions, line, scrW - 40, y); 27.47 + vwfPuts(&vwfOptions, "Left/Right: change; Rotate: close", 27.48 + 4, scrH - 12); 27.49 + 27.50 + while (!done && y <= scrH - 24) { 27.51 + int c = *page++; 27.52 + 27.53 + // Break at newline and at end of text 27.54 + if (c == '\n' || c == 0) { 27.55 + 27.56 + // Terminate the line of text and print it 27.57 + line[linePos] = 0; 27.58 + vwfPuts(&vwfOptions, line, 4, y); 27.59 + 27.60 + y += linePos ? 12 : 6; // Line feed 27.61 + linePos = 0; // Carriage return 27.62 + } else { 27.63 + if (linePos + 2 < sizeof(line)) { 27.64 + line[linePos++] = c; 27.65 + } 27.66 + } 27.67 + if (c == 0) { 27.68 + done = 1; 27.69 + } 27.70 + } 27.71 +} 27.72 + 27.73 +LJBits debriefHandleKeys(void) { 27.74 + int j = readPad(0); 27.75 + if (j & (KEY_START << 16)) { 27.76 + j |= VKEY_ROTR; 27.77 + } 27.78 + vsync(); 27.79 + return j; 27.80 +}
28.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 28.2 +++ b/src/dsjoy.c Fri Mar 13 00:39:12 2009 -0700 28.3 @@ -0,0 +1,48 @@ 28.4 +#include "ljcontrol.h" 28.5 +#ifdef ARM9 28.6 +#include <nds.h> 28.7 +#else 28.8 +#include <gba.h> 28.9 +#endif 28.10 + 28.11 +LJBits readHWKeys(void); 28.12 + 28.13 +LJBits readPad(unsigned int player) { 28.14 + LJBits hwKeys = readHWKeys(); 28.15 + LJBits out = 0; 28.16 + 28.17 + if (hwKeys & KEY_UP) { 28.18 + out |= VKEY_UP; 28.19 + } 28.20 + if (hwKeys & KEY_DOWN) { 28.21 + out |= VKEY_DOWN; 28.22 + } 28.23 + if (hwKeys & KEY_LEFT) { 28.24 + out |= VKEY_LEFT; 28.25 + } 28.26 + if (hwKeys & KEY_RIGHT) { 28.27 + out |= VKEY_RIGHT; 28.28 + } 28.29 + if (hwKeys & KEY_B) { 28.30 + out |= VKEY_ROTL; 28.31 + } 28.32 + if (hwKeys & KEY_A) { 28.33 + out |= VKEY_ROTR; 28.34 + } 28.35 + if (hwKeys & (KEY_L | KEY_R)) { 28.36 + out |= VKEY_HOLD; 28.37 + } 28.38 + 28.39 +#ifdef ARM9 28.40 + if (hwKeys & KEY_X) { 28.41 + out |= VKEY_MACRO(3); 28.42 + } 28.43 + if (hwKeys & KEY_Y) { 28.44 + out |= VKEY_MACRO(2); 28.45 + } 28.46 +#endif 28.47 + 28.48 + // on the GBA and DS, we need to add the console buttons in 28.49 + out |= hwKeys << 16; 28.50 + return out; 28.51 +}
29.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 29.2 +++ b/src/dssleep.c Fri Mar 13 00:39:12 2009 -0700 29.3 @@ -0,0 +1,35 @@ 29.4 +#include <nds.h> 29.5 + 29.6 +/* 29.7 +by Mighty Max and melw, 29.8 +after http://forum.gbadev.org/viewtopic.php?t=12011 29.9 +*/ 29.10 + 29.11 +int needLidSleep(void) { 29.12 + // when reading keys 29.13 + if (IPC->buttons & 0x0080) { 29.14 + /* hinge is closed */ 29.15 + 29.16 + /* Step 1: Turn off all interrupts but vblank for waking up */ 29.17 + unsigned long oldIE = REG_IE; 29.18 + REG_IE = IRQ_VBLANK; 29.19 + 29.20 + /* Step 2: Power off most of the system */ 29.21 + powerOFF(POWER_LCD); 29.22 + 29.23 + /* Step 3: Wait one vblank at a time until the ARM7 tells us 29.24 + that the lid is no longer closed */ 29.25 + while (IPC->buttons & 0x0080) { 29.26 + swiWaitForVBlank(); 29.27 + } 29.28 + 29.29 + /* Step 4: Wait a bit more (necessary?) */ 29.30 + swiWaitForVBlank(); 29.31 + 29.32 + /* Step 5: Restore old machine state */ 29.33 + powerON(POWER_LCD); 29.34 + REG_IE = oldIE; 29.35 + return 1; 29.36 + } 29.37 + return 0; 29.38 +}
30.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 30.2 +++ b/src/dssound7.c Fri Mar 13 00:39:12 2009 -0700 30.3 @@ -0,0 +1,429 @@ 30.4 +/*--------------------------------------------------------------------------------- 30.5 + 30.6 + LOCKJAW Tetromino Game: ARM7 sound code 30.7 + 30.8 + based on 30.9 + default ARM7 core 30.10 + 30.11 + Copyright (C) 2007 30.12 + Damian Yerrick (tepples) 30.13 + 30.14 + This software is provided 'as-is', without any express or implied 30.15 + warranty. In no event will the authors be held liable for any 30.16 + damages arising from the use of this software. 30.17 + 30.18 + Permission is granted to anyone to use this software for any 30.19 + purpose, including commercial applications, and to alter it and 30.20 + redistribute it freely, subject to the following restrictions: 30.21 + 30.22 + 1. The origin of this software must not be misrepresented; you 30.23 + must not claim that you wrote the original software. If you use 30.24 + this software in a product, an acknowledgment in the product 30.25 + documentation would be appreciated but is not required. 30.26 + 2. Altered source versions must be plainly marked as such, and 30.27 + must not be misrepresented as being the original software. 30.28 + 3. This notice may not be removed or altered from any source 30.29 + distribution. 30.30 + 30.31 +---------------------------------------------------------------------------------*/ 30.32 +#include <nds.h> 30.33 +#include <sys/types.h> 30.34 + 30.35 +extern const unsigned short midi2psgFreq[120]; 30.36 + 30.37 +#define C(octave) (octave * 12 + 0) 30.38 +#define CS(octave) (octave * 12 + 1) 30.39 +#define D(octave) (octave * 12 + 2) 30.40 +#define DS(octave) (octave * 12 + 3) 30.41 +#define E(octave) (octave * 12 + 4) 30.42 +#define F(octave) (octave * 12 + 5) 30.43 +#define FS(octave) (octave * 12 + 6) 30.44 +#define G(octave) (octave * 12 + 7) 30.45 +#define GS(octave) (octave * 12 + 8) 30.46 +#define A(octave) (octave * 12 + 9) 30.47 +#define AS(octave) (octave * 12 + 10) 30.48 +#define B(octave) (octave * 12 + 11) 30.49 + 30.50 + 30.51 +#define N_MUSIC_CHANNELS 2 30.52 + 30.53 +static unsigned short chNote[N_MUSIC_CHANNELS]; 30.54 +static unsigned char chVol[N_MUSIC_CHANNELS]; 30.55 + 30.56 + 30.57 +static const unsigned char startVol[N_MUSIC_CHANNELS] = {32, 30}; 30.58 +static const unsigned char fade[N_MUSIC_CHANNELS] = {0, 1}; 30.59 +static const unsigned char chDuty[N_MUSIC_CHANNELS] = {0, 3}; 30.60 + 30.61 +static const signed char bassLine[64] = { 30.62 + A(2), 0, A(3), 0, -1, 0, A(2), 0, 30.63 + A(3), 0, -1, 0, 0, 0, 0, 0, 30.64 + 0, 0, 0, 0, 0, 0, 0, 0, 30.65 + C(3), 0, C(4), 0, G(2), 0, G(3), 0, 30.66 + A(2), 0, A(3), 0, -1, 0, A(2), 0, 30.67 + A(3), 0, -1, 0, 0, 0, 0, 0, 30.68 + 0, 0, 0, 0, 0, 0, 0, 0, 30.69 + C(3), 0, C(4), 0, G(2), 0, G(3), 0, 30.70 +}; 30.71 + 30.72 +static const signed char mel1[64] = { 30.73 + E(6), 0, 0, 0, B(5), 0, C(6), 0, 30.74 + D(6), 0, 0, 0, C(6), 0, B(5), 0, 30.75 + A(5), 0, 0, 0, A(5), 0, C(6), 0, 30.76 + E(6), 0, 0, 0, D(6), 0, C(6), 0, 30.77 + B(5), 0, 0, 0, 0, 0, C(6), 0, 30.78 + D(6), 0, 0, 0, E(6), 0, 0, 0, 30.79 + C(6), 0, 0, 0, A(5), 0, 0, 0, 30.80 + A(5) 30.81 +}; 30.82 + 30.83 +static const signed char mel2[64] = { 30.84 + 0, 0, D(6), 0, 0, 0, F(6), 0, 30.85 + A(6), 0, 0, 0, G(6), 0, F(6), 0, 30.86 + E(6), 0, 0, 0, 0, 0, C(6), 0, 30.87 + E(6), 0, 0, 0, D(6), 0, C(6), 0, 30.88 + B(5), 0, 0, 0, B(5), 0, C(6), 0, 30.89 + D(6), 0, 0, 0, E(6), 0, 0, 0, 30.90 + C(6), 0, 0, 0, A(5), 0, 0, 0, 30.91 + A(5) 30.92 +}; 30.93 + 30.94 +static const signed char *const scripts[4][N_MUSIC_CHANNELS] = { 30.95 + { bassLine, mel1 }, 30.96 + { bassLine, mel1 }, 30.97 + { bassLine, mel2 }, 30.98 + { bassLine, mel2 } 30.99 +}; 30.100 + 30.101 +int order = 0; 30.102 +char ticksLeft = 1; 30.103 +char row = 0; 30.104 +char musicPaused = 1; 30.105 + 30.106 +/* 30.107 + * Plays a repeating drum track and bass track. 30.108 + */ 30.109 +void ljMusic(void) { 30.110 + if (musicPaused) { 30.111 + for (int ch = 0; ch < N_MUSIC_CHANNELS; ++ch) { 30.112 + chVol[ch] = 0; 30.113 + } 30.114 + } else if (--ticksLeft <= 0) { 30.115 + ticksLeft += 6; 30.116 + for (int ch = 0; ch < N_MUSIC_CHANNELS; ++ch) { 30.117 + const signed char *script = scripts[order][ch]; 30.118 + int note = script ? script[(int)row] : 0; 30.119 + if (note < 0) { 30.120 + chVol[ch] = 0; 30.121 + } else if (note > 0) { 30.122 + chVol[ch] = startVol[ch]; 30.123 + chNote[ch] = midi2psgFreq[note]; 30.124 + } 30.125 + } 30.126 + ++row; 30.127 + if (row >= 64) { 30.128 + row = 0; 30.129 + ++order; 30.130 + if (order == 4) { 30.131 + order = 0; 30.132 + } 30.133 + } 30.134 + } 30.135 + 30.136 + 30.137 + // Play notes 30.138 + for (int ch = 0; ch < N_MUSIC_CHANNELS; ++ch) { 30.139 + SCHANNEL_CR(8 + ch) = SCHANNEL_ENABLE | SOUND_FORMAT_PSG | SOUND_PAN(64) 30.140 + | (chDuty[ch] << 24) | chVol[ch]; 30.141 + SCHANNEL_TIMER(8 + ch) = chNote[ch]; 30.142 + 30.143 + // update volumes 30.144 + if (chVol[ch] > fade[ch]) { 30.145 + chVol[ch] -= fade[ch]; 30.146 + } else { 30.147 + chVol[ch] = 0; 30.148 + } 30.149 + } 30.150 +} 30.151 + 30.152 +#define FX(note, volume, duty) ((note) | ((volume) << 7) | ((duty) << 14)) 30.153 + 30.154 +static const u16 rotateEffect[] = { 30.155 + 0, 6, 30.156 + FX(A(5), 32, 1), FX(A(5), 8, 1), 30.157 + FX(D(6), 32, 1), FX(D(6), 8, 1), 30.158 + FX(G(6), 32, 1), FX(G(6), 8, 1) 30.159 +}; 30.160 + 30.161 +static const u16 moveEffect[] = { 30.162 + 0, 2, 30.163 + FX(A(6), 32, 1), FX(A(6), 8, 1) 30.164 +}; 30.165 + 30.166 +static const u16 landEffect[] = { 30.167 + 0, 8, 30.168 + FX(C(4), 64, 3), FX(GS(3), 56, 3), 30.169 + FX(F(3), 48, 3), FX(D(3), 40, 3), 30.170 + FX(C(3), 32, 3), FX(B(2), 24, 3), 30.171 + FX(C(3), 16, 3), FX(B(2), 8, 3), 30.172 +}; 30.173 + 30.174 +static const u16 lockEffect[] = { 30.175 + 1, 2, 30.176 + FX(C(8), 16, 1), FX(C(8), 4, 1) 30.177 +}; 30.178 + 30.179 +static const u16 lineEffect[] = { 30.180 + 0, 15, 30.181 + FX(C(6), 64, 2), FX(DS(6), 59, 2), FX(F(6), 54, 2), 30.182 + FX(C(6), 49, 2), FX(D(6), 44, 2), FX(F(6), 39, 2), 30.183 + FX(C(6), 34, 3), FX(DS(6), 29, 3), FX(F(6), 24, 3), 30.184 + FX(C(6), 20, 3), FX(D(6), 16, 3), FX(F(6), 12, 3), 30.185 + FX(C(6), 9, 3), FX(DS(6), 6, 3), FX(F(6), 3, 3) 30.186 +}; 30.187 + 30.188 +static const u16 homerEffect[] = { 30.189 + 0, 25, 30.190 + FX(C(6), 64, 2), FX(DS(6), 59, 2), FX(F(6), 54, 2), 30.191 + FX(C(6), 49, 2), FX(D(6), 44, 2), FX(F(6), 39, 2), 30.192 + FX(C(6), 34, 3), FX(DS(6), 29, 3), FX(F(6), 24, 3), 30.193 + FX(C(6), 20, 3), 30.194 + FX(DS(6), 64, 2), FX(FS(6), 59, 2), FX(GS(6), 54, 2), 30.195 + FX(DS(6), 49, 2), FX(F(6), 44, 2), FX(GS(6), 39, 2), 30.196 + FX(DS(6), 34, 3), FX(FS(6), 29, 3), FX(GS(6), 24, 3), 30.197 + FX(DS(6), 20, 3), FX(F(6), 16, 3), FX(GS(6), 12, 3), 30.198 + FX(DS(6), 9, 3), FX(F(6), 6, 3), FX(GS(6), 3, 3) 30.199 +}; 30.200 + 30.201 +static const u16 streakEffect[] = { 30.202 + 0, 35, 30.203 + FX(C(6), 64, 2), FX(DS(6), 59, 2), FX(F(6), 54, 2), 30.204 + FX(C(6), 49, 2), FX(D(6), 44, 2), FX(F(6), 39, 2), 30.205 + FX(C(6), 34, 3), FX(DS(6), 29, 3), FX(F(6), 24, 3), 30.206 + FX(C(6), 20, 3), 30.207 + FX(DS(6), 64, 2), FX(FS(6), 59, 2), FX(GS(6), 54, 2), 30.208 + FX(DS(6), 49, 2), FX(F(6), 44, 2), FX(GS(6), 39, 2), 30.209 + FX(DS(6), 34, 3), FX(FS(6), 29, 3), FX(GS(6), 24, 3), 30.210 + FX(DS(6), 20, 3), 30.211 + FX(FS(6), 64, 2), FX(A(6), 59, 2), FX(B(6), 54, 2), 30.212 + FX(FS(6), 49, 2), FX(GS(6), 44, 2), FX(B(6), 39, 2), 30.213 + FX(FS(6), 34, 3), FX(A(6), 29, 3), FX(B(6), 24, 3), 30.214 + FX(FS(6), 20, 3), FX(GS(6), 16, 3), FX(B(6), 12, 3), 30.215 + FX(FS(6), 9, 3), FX(GS(6), 6, 3), FX(B(6), 3, 3) 30.216 +}; 30.217 + 30.218 +static const u16 holdEffect[] = { 30.219 + 1, 10, 30.220 + FX(E(5), 20, 0), FX(G(5), 25, 0), 30.221 + FX(B(5), 30, 0), FX(B(5), 30, 0), 30.222 + FX(G(5), 28, 0), FX(E(5), 24, 0), 30.223 + FX(D(5), 20, 0), FX(C(5), 15, 0), 30.224 + FX(C(5), 10, 0), FX(C(5), 5, 0) 30.225 +}; 30.226 + 30.227 + 30.228 +static const u16 irsEffect[] = { 30.229 + 0, 12, 30.230 + FX(CS(7), 40, 3), FX(CS(7), 40, 3), 30.231 + FX(FS(7), 40, 3), FX(FS(7), 40, 3), 30.232 + FX(B(7), 40, 3), FX(B(7), 35, 3), 30.233 + FX(B(7), 30, 3), FX(B(7), 25, 3), 30.234 + FX(B(7), 20, 3), FX(B(7), 15, 3), 30.235 + FX(B(7), 10, 3), FX(B(7), 5, 3), 30.236 +}; 30.237 + 30.238 +static const u16 countEffect[] = { 30.239 + 0, 14, 30.240 + FX(D(5), 64, 0), FX(D(5), 64, 0), 30.241 + FX(CS(5), 64, 0), FX(CS(5), 64, 0), 30.242 + FX(C(5), 64, 1), FX(C(5), 64, 1), 30.243 + FX(B(4), 64, 1), FX(AS(4), 64, 1), 30.244 + FX(A(4), 64, 2), FX(GS(4), 64, 2), 30.245 + FX(GS(4), 64, 3), FX(G(4), 48, 3), 30.246 + FX(G(4), 32, 3), FX(G(4), 16, 3), 30.247 +}; 30.248 + 30.249 +static const u16 winEffect1[] = { 30.250 + 0, 54, 30.251 + FX(C(6), 32, 2), FX(C(6), 48, 1), FX(C(6), 48, 1), FX(C(6), 48, 1), 30.252 + FX(C(6), 32, 2), 0, 0, 0, 0, 30.253 + FX(AS(5), 32, 2), FX(AS(5), 48, 1), FX(AS(5), 48, 1), FX(AS(5), 48, 1), 30.254 + FX(AS(5), 32, 2), 0, 0, 0, 0, 30.255 + FX(C(6), 32, 2), FX(C(6), 48, 1), FX(C(6), 48, 1), FX(C(6), 48, 1), 30.256 + FX(D(6), 32, 2), 0, 0, 0, 0, 30.257 + 30.258 + FX(D(6), 32, 2), FX(D(6), 48, 1), FX(D(6), 48, 1), FX(D(6), 48, 1), 30.259 + FX(D(6), 47, 1), FX(D(6), 46, 1), FX(D(6), 45, 1), FX(D(6), 44, 1), 30.260 + FX(D(6), 43, 1), FX(D(6), 42, 1), FX(D(6), 41, 1), FX(D(6), 40, 1), 30.261 + FX(D(6), 39, 1), FX(D(6), 38, 1), FX(D(6), 37, 1), FX(D(6), 36, 1), 30.262 + FX(D(6), 35, 1), FX(D(6), 34, 1), FX(D(6), 33, 1), FX(D(6), 32, 1), 30.263 + FX(D(6), 31, 1), FX(D(6), 30, 1), FX(D(6), 29, 1), FX(D(6), 28, 1), 30.264 + FX(D(6), 27, 1), FX(D(6), 26, 1), FX(D(6), 25, 1), FX(D(6), 24, 1), 30.265 + FX(D(6), 18, 2), FX(D(6), 12, 2), FX(D(6), 6, 2) 30.266 +}; 30.267 + 30.268 +static const u16 winEffect2[] = { 30.269 + 0, 54, 30.270 + FX(E(6), 32, 2), FX(E(6), 48, 1), FX(E(6), 48, 1), FX(E(6), 48, 1), 30.271 + FX(E(6), 32, 2), 0, 0, 0, 0, 30.272 + FX(D(6), 32, 2), FX(D(6), 48, 1), FX(D(6), 48, 1), FX(D(6), 48, 1), 30.273 + FX(D(6), 32, 2), 0, 0, 0, 0, 30.274 + FX(E(6), 32, 2), FX(E(6), 48, 1), FX(E(6), 48, 1), FX(E(6), 48, 1), 30.275 + FX(E(6), 32, 2), 0, 0, 0, 0, 30.276 + 30.277 + FX(FS(6), 32, 2), FX(FS(6), 48, 1), FX(FS(6), 48, 1), FX(FS(6), 48, 1), 30.278 + FX(FS(6), 47, 1), FX(FS(6), 46, 1), FX(FS(6), 45, 1), FX(FS(6), 44, 1), 30.279 + FX(FS(6), 43, 1), FX(FS(6), 42, 1), FX(FS(6), 41, 1), FX(FS(6), 40, 1), 30.280 + FX(FS(6), 39, 1), FX(FS(6), 38, 1), FX(FS(6), 37, 1), FX(FS(6), 36, 1), 30.281 + FX(FS(6), 35, 1), FX(FS(6), 34, 1), FX(FS(6), 33, 1), FX(FS(6), 32, 1), 30.282 + FX(FS(6), 31, 1), FX(FS(6), 30, 1), FX(FS(6), 29, 1), FX(FS(6), 28, 1), 30.283 + FX(FS(6), 27, 1), FX(FS(6), 26, 1), FX(FS(6), 25, 1), FX(FS(6), 24, 1), 30.284 + FX(FS(6), 18, 2), FX(FS(6), 12, 2), FX(FS(6), 6, 2) 30.285 +}; 30.286 + 30.287 +static const u16 dieEffect1[] = { 30.288 + 0, 60, 30.289 + FX(E(3), 60, 2), FX(E(3), 59, 2), FX(E(3), 58, 2), FX(E(3), 57, 2), 30.290 + FX(E(3), 56, 2), FX(E(3), 55, 2), FX(E(3), 54, 2), FX(E(3), 53, 2), 30.291 + FX(E(3), 52, 2), FX(E(3), 51, 2), FX(E(3), 50, 2), FX(E(3), 49, 2), 30.292 + FX(E(3), 48, 2), FX(E(3), 47, 2), FX(E(3), 46, 2), FX(E(3), 45, 2), 30.293 + FX(E(3), 44, 2), FX(E(3), 43, 2), FX(E(3), 42, 2), FX(E(3), 41, 2), 30.294 + FX(E(3), 40, 2), FX(E(3), 39, 2), FX(E(3), 38, 2), FX(E(3), 37, 2), 30.295 + FX(E(3), 36, 2), FX(E(3), 35, 2), FX(E(3), 34, 2), FX(E(3), 33, 2), 30.296 + FX(E(3), 32, 2), FX(E(3), 31, 2), FX(E(3), 30, 2), FX(E(3), 29, 2), 30.297 + FX(E(3), 28, 2), FX(E(3), 27, 2), FX(E(3), 26, 2), FX(E(3), 25, 2), 30.298 + FX(E(3), 24, 2), FX(E(3), 23, 2), FX(E(3), 22, 2), FX(E(3), 21, 2), 30.299 + FX(E(3), 20, 2), FX(E(3), 19, 2), FX(E(3), 18, 2), FX(E(3), 17, 2), 30.300 + FX(E(3), 16, 2), FX(E(3), 15, 2), FX(E(3), 14, 2), FX(E(3), 13, 2), 30.301 + FX(E(3), 12, 2), FX(E(3), 11, 2), FX(E(3), 10, 2), FX(E(3), 9, 2), 30.302 + FX(E(3), 8, 2), FX(E(3), 7, 2), FX(E(3), 6, 2), FX(E(3), 5, 2), 30.303 + FX(E(3), 4, 2), FX(E(3), 3, 2), FX(E(3), 2, 2), FX(E(3), 1, 2) 30.304 +}; 30.305 + 30.306 +static const u16 dieEffect2[] = { 30.307 + 1, 60, 30.308 + FX(E(4), 60, 2), FX(B(3), 59, 2), FX(E(3), 58, 2), FX(E(3), 57, 2), 30.309 + FX(E(3), 56, 2), FX(E(3), 55, 2), FX(E(3), 54, 2), FX(E(3), 53, 2), 30.310 + FX(E(3), 52, 2), FX(E(3), 51, 2), FX(E(3), 50, 2), FX(E(3), 49, 2), 30.311 + FX(E(3), 48, 2), FX(E(3), 47, 2), FX(E(3), 46, 2), FX(E(3), 45, 2), 30.312 + FX(E(3), 44, 2), FX(E(3), 43, 2), FX(E(3), 42, 2), FX(E(3), 41, 2), 30.313 + FX(E(3), 40, 2), FX(E(3), 39, 2), FX(E(3), 38, 2), FX(E(3), 37, 2), 30.314 + FX(E(3), 36, 2), FX(E(3), 35, 2), FX(E(3), 34, 2), FX(E(3), 33, 2), 30.315 + FX(E(3), 32, 2), FX(E(3), 31, 2), FX(E(3), 30, 2), FX(E(3), 29, 2), 30.316 + FX(E(3), 28, 2), FX(E(3), 27, 2), FX(E(3), 26, 2), FX(E(3), 25, 2), 30.317 + FX(E(3), 24, 2), FX(E(3), 23, 2), FX(E(3), 22, 2), FX(E(3), 21, 2), 30.318 + FX(E(3), 20, 2), FX(E(3), 19, 2), FX(E(3), 18, 2), FX(E(3), 17, 2), 30.319 + FX(E(3), 16, 2), FX(E(3), 15, 2), FX(E(3), 14, 2), FX(E(3), 13, 2), 30.320 + FX(E(3), 12, 2), FX(E(3), 11, 2), FX(E(3), 10, 2), FX(E(3), 9, 2), 30.321 + FX(E(3), 8, 2), FX(E(3), 7, 2), FX(E(3), 6, 2), FX(E(3), 5, 2), 30.322 + FX(E(3), 4, 2), FX(E(3), 3, 2), FX(E(3), 2, 2), FX(E(3), 1, 2) 30.323 +}; 30.324 + 30.325 +static const u16 *effects[32] = { 30.326 + rotateEffect, 30.327 + moveEffect, 30.328 + NULL, // drop: not used 30.329 + landEffect, 30.330 + lockEffect, 30.331 + lineEffect, 30.332 + homerEffect, 30.333 + streakEffect, 30.334 + NULL, // spawn 30.335 + holdEffect, // hold 30.336 + irsEffect, // irs 30.337 + NULL, // 4x4 square 30.338 + NULL, NULL, NULL, NULL, // unused 30.339 + dieEffect1, dieEffect2 30.340 +}; 30.341 + 30.342 + 30.343 + 30.344 +static const u16 *fxPtr[5]; 30.345 +static u8 fxLen[5]; 30.346 +static s8 lastCD; 30.347 + 30.348 + 30.349 + 30.350 +static void startEffect(const u16 *s) { 30.351 + 30.352 + // skip effects that lack an actual effect 30.353 + if (!s) { 30.354 + return; 30.355 + } 30.356 + 30.357 + unsigned int type = s[0]; 30.358 + unsigned int len = s[1]; 30.359 + unsigned int min = 0; 30.360 + unsigned int max = 2; 30.361 + 30.362 + if (type > 0) { 30.363 + min = 3; 30.364 + max = 4; 30.365 + } 30.366 + 30.367 + // prepare to kill the channel with the shortest time remaining 30.368 + unsigned int shortestLen = fxLen[min]; 30.369 + unsigned int shortestCh = min; 30.370 + for (int ch = min + 1; ch <= max; ++ch) { 30.371 + if (fxLen[ch] < shortestLen) { 30.372 + shortestLen = fxLen[ch]; 30.373 + shortestCh = ch; 30.374 + } 30.375 + } 30.376 + 30.377 + // if the channel is suitable, kill it 30.378 + if (shortestLen < len) { 30.379 + fxPtr[shortestCh] = s + 2; 30.380 + fxLen[shortestCh] = len; 30.381 + } 30.382 +} 30.383 + 30.384 +static void pollEffects(void) { 30.385 + for (int ch = 0; ch < 5; ++ch) { 30.386 + unsigned int note = 36; 30.387 + unsigned int vol = 0; 30.388 + unsigned int duty = 0; 30.389 + if (fxLen[ch] > 0) { 30.390 + unsigned int data = *fxPtr[ch]++; 30.391 + duty = (data >> 14) & 0x03; 30.392 + vol = (data >> 7) & 0x7F; 30.393 + note = data & 0x7F; 30.394 + --fxLen[ch]; 30.395 + } 30.396 + SCHANNEL_CR(11 + ch) = SCHANNEL_ENABLE | SOUND_FORMAT_PSG | SOUND_PAN(64) 30.397 + | (duty << 24) | vol; 30.398 + SCHANNEL_TIMER(11 + ch) = midi2psgFreq[note]; 30.399 + } 30.400 +} 30.401 + 30.402 +void ljSoundEffects(unsigned long int sounds, 30.403 + int countdown) { 30.404 + 30.405 + // cancel out overlapping line clear sounds 30.406 + if (sounds & 0x0080) { 30.407 + sounds &= ~0x0060; 30.408 + } else if (sounds & 0x0040) { 30.409 + sounds &= ~0x0020; 30.410 + } 30.411 + 30.412 + for (int y = 0; sounds; ++y, sounds >>= 1) { 30.413 + if (sounds & 1) { 30.414 + startEffect(effects[y]); 30.415 + } 30.416 + } 30.417 + if (countdown < 0) { 30.418 + countdown = 0; 30.419 + } else if (countdown > 6) { 30.420 + countdown = 6; 30.421 + } 30.422 + if (countdown < lastCD) { 30.423 + if (countdown > 0) { 30.424 + startEffect(countEffect); 30.425 + } else { 30.426 + startEffect(winEffect1); 30.427 + startEffect(winEffect2); 30.428 + } 30.429 + } 30.430 + lastCD = countdown; 30.431 + pollEffects(); 30.432 +}
31.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 31.2 +++ b/src/explode.c Fri Mar 13 00:39:12 2009 -0700 31.3 @@ -0,0 +1,142 @@ 31.4 +/* 31.5 +Bombl*ss extension for LOCKJAW Tetromino Game 31.6 +Copr. 2007 Damian Yerrick 31.7 + 31.8 +*/ 31.9 + 31.10 + 31.11 +/* 31.12 + 31.13 +During falling, the auxiliary playfield p->c[][] contains a map of 31.14 +individual regions used by the falling code. But during explosion, 31.15 +it serves a different purpose: counting the number of frames before 31.16 +a given block is erased. 31.17 + 31.18 +*/ 31.19 + 31.20 +const char explodeWidth[nLines] = { 31.21 + 4, // megabomb 31.22 + 3, 3, 3, 3, 4, 5, 5, 6, 6, 7 // 1-10 lines 31.23 +}; 31.24 +const char explodeHeight[nLines] = { 31.25 + 3, // megabomb 31.26 + 0, 1, 2, 3, 4, 5, 5, 6, 6, 7 // 1-10 lines 31.27 +}; 31.28 + 31.29 + 31.30 +/** 31.31 + * Sets the number of frames before a block is erased based on 31.32 + * the explosion. 31.33 + * @param p the playfield 31.34 + * @param x the column where the bomb is centered 31.35 + * @param y the row where the bomb is centered 31.36 + * @param bombColor the color of the bomb 31.37 + * @param nLines the number of lines cleared, which determines the explosion size 31.38 + */ 31.39 +void explodeAt(LJField *p, int x, int y, int bombColor, int nLines) { 31.40 + int boom = nLines; 31.41 + 31.42 + if ((bombColor & COLOR_MASK) == BOMBCOLOR_SQUARE) { 31.43 + boom = 4; 31.44 + } else if (boom > 10) { 31.45 + boom = 10; 31.46 + } 31.47 + 31.48 + int boomLeft = x - explodeWidth[boom]; 31.49 + if (boomLeft < p->leftWall) { 31.50 + boomLeft = p->leftWall; 31.51 + } 31.52 + int boomRight = x + explodeWidth[boom]; 31.53 + if (boomRight > p->rightWall - 1) { 31.54 + boomLeft = p->rightWall - 1; 31.55 + } 31.56 + int boomBottom = y - explodeHeight[boom]; 31.57 + if (boomLeft < 0) { 31.58 + boomLeft = 0; 31.59 + } 31.60 + int boomTop = y + explodeHeight[boom]; 31.61 + if (boomRight > LJ_PF_HT - 1) { 31.62 + boomLeft = LJ_PF_HT - 1; 31.63 + } 31.64 + 31.65 + for (by = boomBottom; bx < boomTop; ++bx) { 31.66 + int dy = y - by; 31.67 + int dy2 = dy * dy; 31.68 + for (bx = boomLeft; bx < boomRight; ++bx) { 31.69 + int dx = x - bx; 31.70 + int dx2 = dx * dx; 31.71 + unsigned int time = (dx2 + dy2) / EXPLODE_SPEED + 2; 31.72 + if (p->c[y][x] > time) { 31.73 + p->c[y][x] = time; 31.74 + } 31.75 + } 31.76 + } 31.77 +} 31.78 + 31.79 +static int blockIsBomb(unsigned int blk) { 31.80 + return (blk & COLOR_MASK) == BOMBCOLOR_NORMAL 31.81 + || (blk & COLOR_MASK) == BOMBCOLOR_SQUARE; 31.82 +} 31.83 + 31.84 +/** 31.85 + * Scans the exploding areas, erases blocks, and sets up bombs to explode. 31.86 + * @param p the playfield 31.87 + * @return the rows that were changed 31.88 + */ 31.89 +LJBits explodeFrame(LJField *p) { 31.90 + LJBits changedRows = 0; 31.91 + LJBits changeAny = 0; 31.92 + 31.93 + int nLines = countRows(p->tempRows); 31.94 + 31.95 + // first pass: destruction 31.96 + for (int y = 0; y < LJ_PF_HT; ++y) { 31.97 + for (int x = p->leftWall; x < p->rightWall; ++x) { 31.98 + if (p->c[y][x] == 1) { 31.99 + unsigned int blk = p->b[y][x]; 31.100 + 31.101 + if (blk) { 31.102 + changedRows |= 1 << y; 31.103 + p->b[y][x] = 0; 31.104 + if (blockIsBomb(blk)) { 31.105 + explodeAt(p, x, y, blk, nLines); 31.106 + } 31.107 + } 31.108 + } 31.109 + } 31.110 + } 31.111 + 31.112 + // second pass: countdown timer per block 31.113 + for (int y = 0; y < LJ_PF_HT; ++y) { 31.114 + for (int x = p->leftWall; x < p->rightWall; ++x) { 31.115 + if (p->c[y][x] > 0) { 31.116 + --p->c[y][x] > 0; 31.117 + changeAny = 1; 31.118 + } 31.119 + } 31.120 + } 31.121 + 31.122 + return changeAny ? (1 << (LJ_PF_HT - 1)) : changedRows; 31.123 +} 31.124 + 31.125 +/** 31.126 + * Scans the cleared lines for bombs and marks c[] to trigger them. 31.127 + * @param p the playfield, where tempRows holds the cleared lines 31.128 + * @return the rows where bombs were triggered 31.129 + */ 31.130 +LJBits triggerBombs(LJField *p) { 31.131 + LJBits bombRows = 0; 31.132 + 31.133 + for (int y = 0; y < LJ_PF_HT; ++y) { 31.134 + if (p->tempRows & (1 << y)) { 31.135 + for (int x = p->leftWall; x < p->rightWall; ++x) { 31.136 + bool isBomb = blockIsBomb(p->b[y][x]); 31.137 + p->c[y][x] = isBomb; 31.138 + if (isBomb) { 31.139 + bombRows |= 1 << y; 31.140 + } 31.141 + } 31.142 + } 31.143 + } 31.144 + return bombRows; 31.145 +}
32.1 Binary file src/font.bmp has changed
33.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 33.2 +++ b/src/fontdraw.c Fri Mar 13 00:39:12 2009 -0700 33.3 @@ -0,0 +1,372 @@ 33.4 +/* 33.5 +Variable width font drawing library for DS (and GBA) 33.6 + 33.7 +Copyright 2007-2008 Damian Yerrick <pinoandchester@pineight.com> 33.8 + 33.9 +This work is provided 'as-is', without any express or implied 33.10 +warranty. In no event will the authors be held liable for any 33.11 +damages arising from the use of this work. 33.12 + 33.13 +Permission is granted to anyone to use this work for any purpose, 33.14 +including commercial applications, and to alter it and redistribute 33.15 +it freely, subject to the following restrictions: 33.16 + 33.17 +1. The origin of this work must not be misrepresented; you must 33.18 + not claim that you wrote the original work. If you use 33.19 + this work in a product, an acknowledgment in the product 33.20 + documentation would be appreciated but is not required. 33.21 +2. Altered source versions must be plainly marked as such, and must 33.22 + not be misrepresented as being the original work. 33.23 +3. This notice may not be removed or altered from any source 33.24 + distribution. 33.25 + 33.26 +"Source" is the preferred form of a work for making changes to it. 33.27 + 33.28 +*/ 33.29 + 33.30 +#include <string.h> 33.31 +#include <stdio.h> 33.32 +#include "fontdraw.h" 33.33 + 33.34 + 33.35 +extern const unsigned char vwfont_bin[]; 33.36 + 33.37 +typedef struct GlyphRec { 33.38 + unsigned short dataOffset; 33.39 + unsigned char glyphWidth; 33.40 + unsigned char reserved; 33.41 +} GlyphRec; 33.42 + 33.43 +#ifndef FONTDRAW_SPLIT_COMPILE 33.44 +#include "fontdraw_engine.c" 33.45 +#else 33.46 +__attribute__((long_call)) 33.47 +unsigned int fontdraw_putchar(u32 *dst, unsigned int colStride, int wid, int x, int glyph); 33.48 +__attribute__((long_call)) 33.49 +void vwfRectfillColumn(u32 *dst, unsigned int colStride, 33.50 + unsigned int l, unsigned int t, 33.51 + unsigned int r, unsigned int b, 33.52 + unsigned int c); 33.53 +#endif 33.54 + 33.55 + 33.56 +unsigned int fontdraw_charWidth(int glyph) { 33.57 + glyph &= 0xFF; 33.58 + if (glyph < vwfont_bin[1]) { 33.59 + return 0; 33.60 + } 33.61 + glyph -= vwfont_bin[1]; 33.62 + if (vwfont_bin[2] != 0 && glyph >= vwfont_bin[2]) { 33.63 + return 0; 33.64 + } 33.65 + const GlyphRec *glyphRec = 33.66 + ((const GlyphRec *)(vwfont_bin + vwfont_bin[0])) + glyph; 33.67 + return glyphRec->glyphWidth; 33.68 +} 33.69 + 33.70 +unsigned int fontdraw_strWidth(const char *s) { 33.71 + unsigned int width = 0; 33.72 + for (; *s; ++s) { 33.73 + width += fontdraw_charWidth(*s); 33.74 + } 33.75 + return width; 33.76 +} 33.77 + 33.78 +size_t fontdraw_cutStr(const char *s, int targetWidth) { 33.79 + size_t len; 33.80 + for (len = 0; s[len]; ++len) { 33.81 + int charWidth = fontdraw_charWidth(s[len]); 33.82 + if (charWidth > targetWidth) { 33.83 + return len; 33.84 + } 33.85 + targetWidth -= charWidth; 33.86 + } 33.87 + return len; 33.88 +} 33.89 + 33.90 +static unsigned int fontdraw_putline(u32 *dst, unsigned int colStride, int wid, int x, const char *src) { 33.91 + unsigned int startX = x; 33.92 + for (int c = *src; c != 0 && wid > 0; c = *++src) { 33.93 + int chWidth = fontdraw_putchar(dst, colStride, wid, x, c & 0xFF); 33.94 + x += chWidth; 33.95 + wid -= chWidth; 33.96 + } 33.97 + return x - startX; 33.98 +} 33.99 + 33.100 +#ifdef ARM9 33.101 +const VWFWindow vwfTop = { 33.102 + .left = 0, .top = 0, .width = 32, .height = 24, 33.103 + .chrBase = (u32 *)BG_TILE_RAM(0), 33.104 + .map = 31, 33.105 + .core = 0, 33.106 + .mapTileBase = 0 33.107 +}; 33.108 + 33.109 +const VWFWindow vwfTouch = { 33.110 + .left = 0, .top = 0, .width = 32, .height = 24, 33.111 + .chrBase = (u32 *)BG_TILE_RAM_SUB(0), 33.112 + .map = 31, 33.113 + .core = 1, 33.114 + .mapTileBase = 0 33.115 +}; 33.116 +#else 33.117 +const VWFWindow vwfTop = { 33.118 + .left = 0, .top = 0, .width = 30, .height = 20, 33.119 + .chrBase = (u32 *)PATRAM4(0, 0), 33.120 + .map = 31, 33.121 + .core = 0, 33.122 + .mapTileBase = 0 33.123 +}; 33.124 +#endif 33.125 + 33.126 +void vwfRectfill(const VWFWindow *v, int l, int t, int r, int b, int c) 33.127 +{ 33.128 + u32 *dst = v->chrBase; 33.129 + c &= 0x0000000F; 33.130 + c |= c << 4; 33.131 + c |= c << 8; 33.132 + c |= c << 16; 33.133 + 33.134 + unsigned int x = l; 33.135 + unsigned int stride = v->height * 8; 33.136 + u32 *tile = dst + stride * (l >> 3); 33.137 + 33.138 + if (t < 0) { 33.139 + t = 0; 33.140 + } 33.141 + if (b > v->height * 8) { 33.142 + b = v->height * 8; 33.143 + } 33.144 + 33.145 + for(x = l; x < r; x = (x + 8) & -8) { 33.146 + vwfRectfillColumn(tile, stride, x & 7, t, 33.147 + (r & -8) > (x & -8) ? 8 : (r & 7), 33.148 + b, c); 33.149 + tile += stride; 33.150 + } 33.151 +} 33.152 + 33.153 +void vwfHline(const VWFWindow *v, int l, int t, int r, int c) { 33.154 + if (r < l) { 33.155 + int temp = l; 33.156 + l = r; 33.157 + r = temp; 33.158 + } 33.159 + vwfRectfill(v, l, t, r, t + 1, c); 33.160 +} 33.161 + 33.162 +void vwfVline(const VWFWindow *v, int l, int t, int b, int c) { 33.163 + if (b < t) { 33.164 + int temp = t; 33.165 + t = b; 33.166 + b = temp; 33.167 + } 33.168 + vwfRectfill(v, l, t, l + 1, b, c); 33.169 +} 33.170 + 33.171 +void vwfRect(const VWFWindow *v, int l, int t, int r, int b, int c) { 33.172 + vwfVline(v, l, t, b, c); 33.173 + vwfVline(v, r - 1, t, b, c); 33.174 + vwfHline(v, l, t, r, c); 33.175 + vwfHline(v, l, b - 1, r, c); 33.176 +} 33.177 + 33.178 +void vwfWinClear(const VWFWindow *w) { 33.179 + size_t nBytes = 32 * w->width * w->height; 33.180 + memset(w->chrBase, 0, nBytes); 33.181 +} 33.182 + 33.183 +static inline NAMETABLE *vwfGetMapBase(int core, int map) { 33.184 +#if ARM9 33.185 + NAMETABLE *dst = core ? &(MAP_SUB[map]) : &(MAP[map]); 33.186 +#else 33.187 + NAMETABLE *dst = &(MAP[map]); 33.188 +#endif 33.189 + 33.190 + return dst; 33.191 +} 33.192 + 33.193 +void vwfPutMap(const VWFWindow *w, 33.194 + int l, int t, int r, int b, 33.195 + unsigned int orMask) { 33.196 + 33.197 + if (r > (int)w->width) { 33.198 + r = (int)w->width; 33.199 + } 33.200 + if (l < 0) { 33.201 + l = 0; 33.202 + } 33.203 + if (r <= l) { 33.204 + return; 33.205 + } 33.206 + 33.207 + if (b > (int)w->height) { 33.208 + b = (int)w->height; 33.209 + } 33.210 + if (t < 0) { 33.211 + t = 0; 33.212 + } 33.213 + if (b <= t) { 33.214 + return; 33.215 + } 33.216 + 33.217 + NAMETABLE *dst = vwfGetMapBase(w->core, w->map); 33.218 + 33.219 + int mapTile = (w->mapTileBase + w->height * l + t) | orMask; 33.220 + for (int x = w->left + l; x < w->left + r; ++x) { 33.221 + for (int y = w->top + t; y < w->top + b; ++y) { 33.222 + (*dst)[y][x] = mapTile++; 33.223 + } 33.224 + mapTile += w->height + t - b; 33.225 + } 33.226 +} 33.227 + 33.228 +void vwfWinInit(const VWFWindow *w) { 33.229 + vwfWinClear(w); 33.230 + vwfPutMap(w, 0, 0, w->width, w->height, 0x0000); 33.231 +} 33.232 + 33.233 +#if 0 33.234 +void vwfWinInit(const VWFWindow *w) { 33.235 +#if ARM9 33.236 + NAMETABLE *dst = w->core ? &(MAP_SUB[w->map]) : &(MAP[w->map]); 33.237 +#else 33.238 + NAMETABLE *dst = &(MAP[w->map]); 33.239 +#endif 33.240 + 33.241 + int mapTile = w->mapTileBase; 33.242 + for (int x = w->left; x < w->left + w->width; ++x) { 33.243 + for (int y = w->top; y < w->top + w->height; ++y) { 33.244 + (*dst)[y][x] = mapTile++; 33.245 + } 33.246 + } 33.247 + vwfWinClear(w); 33.248 +} 33.249 +#endif 33.250 + 33.251 +unsigned int vwfPuts(const VWFWindow *w, 33.252 + const char *str, 33.253 + int x, int y) { 33.254 + return fontdraw_putline(w->chrBase + y, 8 * w->height, 33.255 + w->width * 8 - x, x, str); 33.256 +} 33.257 + 33.258 +unsigned int vwfPutc(const VWFWindow *w, 33.259 + int c, 33.260 + int x, int y) { 33.261 + int width = 8 * w->width - x; 33.262 + if (width >= fontdraw_charWidth(c)) { 33.263 + return fontdraw_putchar(w->chrBase + y, 8 * w->height, 33.264 + w->width * 8 - x, x, c & 0xFF); 33.265 + } else { 33.266 + return 0; 33.267 + } 33.268 +} 33.269 + 33.270 +// old api emulation 33.271 + 33.272 +#if 0 33.273 +void fontdraw_demo_putline(int x, int y, const char *str) { 33.274 + const VWFWindow *w = &vwfTop; 33.275 +#if ARM9 33.276 + if (y >= 288) { 33.277 + w = &vwfTouch; 33.278 + y -= 288; 33.279 + } 33.280 +#endif 33.281 + vwfPuts(w, str, x, y); 33.282 +} 33.283 +#endif 33.284 + 33.285 +void fontdraw_setupVRAM(int sub) { 33.286 +#if ARM9 33.287 + const VWFWindow *w = sub ? &vwfTouch : &vwfTop; 33.288 +#else 33.289 + const VWFWindow *w = &vwfTop; 33.290 +#endif 33.291 + 33.292 +#if ARM9 33.293 + if (sub == 0) { 33.294 + BGCTRL_SUB[0] = BG_MAP_BASE(31) | BG_TILE_BASE(0); 33.295 + BG_OFFSET_SUB[0].x = 0; 33.296 + BG_OFFSET_SUB[0].y = 0; 33.297 + } else 33.298 +#endif 33.299 + { 33.300 + BGCTRL[0] = BG_MAP_BASE(31) | BG_TILE_BASE(0); 33.301 + BG_OFFSET[0].x = 0; 33.302 + BG_OFFSET[0].y = 0; 33.303 + } 33.304 + 33.305 + vwfWinInit(w); 33.306 +} 33.307 + 33.308 +void fontdraw_cls(int sub) { 33.309 +#if ARM9 33.310 + vwfWinClear(sub ? &vwfTouch : &vwfTop); 33.311 +#else 33.312 + vwfWinClear(&vwfTop); 33.313 +#endif 33.314 +} 33.315 + 33.316 +void vwfBlitAligned(const VWFWindow *src, const VWFWindow *dst, 33.317 + int srcX, int srcY, int dstX, int dstY, 33.318 + int w, int h) { 33.319 + 33.320 + /* Clip in X */ 33.321 + if (srcX < 0) { 33.322 + dstX -= srcX; 33.323 + w -= srcX; 33.324 + srcX = 0; 33.325 + } 33.326 + if (dstX < 0) { 33.327 + srcX -= dstX; 33.328 + w -= dstX; 33.329 + dstX = 0; 33.330 + } 33.331 + if (srcX + w > (int)src->width) { 33.332 + w = src->width - srcX; 33.333 + } 33.334 + if (dstX + w > (int)dst->width) { 33.335 + w = dst->width - dstX; 33.336 + } 33.337 + if (w <= 0) { 33.338 + return; 33.339 + } 33.340 + 33.341 + /* Clip in Y */ 33.342 + if (srcY < 0) { 33.343 + dstY -= srcY; 33.344 + h -= srcY; 33.345 + srcY = 0; 33.346 + } 33.347 + if (dstY < 0) { 33.348 + srcY -= dstY; 33.349 + h -= dstY; 33.350 + dstY = 0; 33.351 + } 33.352 + if (srcY + h > src->height * 8) { 33.353 + h = src->height * 8 - srcY; 33.354 + } 33.355 + if (dstX + w > dst->height * 8) { 33.356 + h = dst->height * 8 - dstY; 33.357 + } 33.358 + if (h < 0) { 33.359 + return; 33.360 + } 33.361 + 33.362 + { 33.363 + const u32 *srcCol = src->chrBase + srcX * src->height * 8 + srcY; 33.364 + u32 *dstCol = dst->chrBase + dstX * dst->height * 8 + dstY; 33.365 + 33.366 + for (; w > 0; --w) { 33.367 + for (unsigned int y = 0; y < h; ++y) { 33.368 + dstCol[y] = srcCol[y]; 33.369 + } 33.370 + srcCol += src->height * 8; 33.371 + dstCol += dst->height * 8; 33.372 + } 33.373 + } 33.374 + 33.375 +}
34.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 34.2 +++ b/src/fontdraw.h Fri Mar 13 00:39:12 2009 -0700 34.3 @@ -0,0 +1,127 @@ 34.4 +/* 34.5 +Variable width font drawing library for DS (and GBA) 34.6 + 34.7 +Copyright 2007 Damian Yerrick <pinoandchester@pineight.com> 34.8 + 34.9 +This work is provided 'as-is', without any express or implied 34.10 +warranty. In no event will the authors be held liable for any 34.11 +damages arising from the use of this work. 34.12 + 34.13 +Permission is granted to anyone to use this work for any purpose, 34.14 +including commercial applications, and to alter it and redistribute 34.15 +it freely, subject to the following restrictions: 34.16 + 34.17 +1. The origin of this work must not be misrepresented; you must 34.18 + not claim that you wrote the original work. If you use 34.19 + this work in a product, an acknowledgment in the product 34.20 + documentation would be appreciated but is not required. 34.21 +2. Altered source versions must be plainly marked as such, and must 34.22 + not be misrepresented as being the original work. 34.23 +3. This notice may not be removed or altered from any source 34.24 + distribution. 34.25 + 34.26 +"Source" is the preferred form of a work for making changes to it. 34.27 + 34.28 +*/ 34.29 + 34.30 +#ifndef FONTDRAW_H 34.31 +#define FONTDRAW_H 34.32 +#include <sys/types.h> 34.33 + 34.34 +#ifdef ARM9 34.35 +// DS specific macros 34.36 +#include <nds.h> 34.37 +#ifndef BG_OFFSET_SUB 34.38 +#define BG_OFFSET_SUB ((bg_scroll *)(0x04001010)) 34.39 +#endif 34.40 + 34.41 +// macros from libgba that didn't make it to libnds 34.42 +#ifndef MAP 34.43 +typedef u16 NAMETABLE[32][32]; 34.44 +#define MAP ((NAMETABLE *)BG_MAP_RAM(0)) 34.45 +#define MAP_SUB ((NAMETABLE *)BG_MAP_RAM_SUB(0)) 34.46 +#endif 34.47 + 34.48 +#else 34.49 +// GBA specific macros 34.50 +#include <gba_video.h> 34.51 + 34.52 +#endif 34.53 + 34.54 +unsigned int fontdraw_charWidth(int glyph); 34.55 +unsigned int fontdraw_strWidth(const char *s); 34.56 + 34.57 +/** 34.58 + * Returns the number of characters in s that fit within targetWidth pixels. 34.59 + */ 34.60 +size_t fontdraw_cutStr(const char *s, int targetWidth); 34.61 + 34.62 +void fontdraw_setupVRAM(int sub); 34.63 + 34.64 +// New API 34.65 + 34.66 +typedef struct VWFWindow { 34.67 + u8 left; // in 8 pixel units on nametable 34.68 + u8 top; // in 8 pixel units on nametable 34.69 + u8 width; // in 8 pixel units on nametable 34.70 + u8 height; // in 8 pixel units on nametable 34.71 + u32 *chrBase; 34.72 + u8 map; // in 2 KiB units on VRAM 34.73 + u8 core; // 0: main; 1: sub 34.74 + u16 mapTileBase; 34.75 +} VWFWindow; 34.76 + 34.77 +void vwfWinInit(const VWFWindow *vwf); 34.78 +void vwfWinClear(const VWFWindow *vwf); 34.79 +/** 34.80 + * Sets up a portion of a window. 34.81 + * @param vwf the window 34.82 + * @param l distance in tiles from the left side of the window to the 34.83 + * left side of the area to be updated 34.84 + * @param t distance in tiles from the top of the window to the 34.85 + * top of the area to be updated 34.86 + * @param r distance in tiles from the left side of the window to the 34.87 + * right side of the area to be updated 34.88 + * @param b distance in tiles from the top of the window to the 34.89 + * bottom of the area to be updated 34.90 + * @param orMask the data to be OR'd with each map space, typically 34.91 + * containing a palette number in bits 12 to 15 34.92 + */ 34.93 +void vwfPutMap(const VWFWindow *vwf, 34.94 + int l, int t, int r, int b, 34.95 + unsigned int orMask); 34.96 + 34.97 +unsigned int vwfPutc(const VWFWindow *w, 34.98 + int c, 34.99 + int x, int y); 34.100 +unsigned int vwfPuts(const VWFWindow *src, 34.101 + const char *str, 34.102 + int x, int y); 34.103 +void vwfRectfill(const VWFWindow *v, 34.104 + int l, int t, int r, int b, 34.105 + int c); 34.106 +void vwfHline(const VWFWindow *v, int l, int t, int r, int c); 34.107 +void vwfVline(const VWFWindow *v, int l, int t, int b, int c); 34.108 +void vwfRect(const VWFWindow *v, int l, int t, int r, int b, int c); 34.109 + 34.110 +/** 34.111 + * Replaces a rectangle of pixels in dst with pixels from src. 34.112 + * @param src the bitmap to copy from 34.113 + * @param dst the bitmap to copy to 34.114 + * @param srcX the left side of the part of src to copy, 34.115 + * in 8-pixel units 34.116 + * @param srcY the top of the part of src to copy, 34.117 + * in pixels 34.118 + * @param dstX the left side of the part of dst to be replaced, 34.119 + * in 8-pixel units 34.120 + * @param dstY the top of the part of src dst to be replaced, 34.121 + * in pixels 34.122 + */ 34.123 +void vwfBlitAligned(const VWFWindow *src, const VWFWindow *dst, 34.124 + int srcX, int srcY, int dstX, int dstY, 34.125 + int w, int h); 34.126 + 34.127 +extern const VWFWindow vwfTop, vwfTouch; 34.128 + 34.129 + 34.130 +#endif
35.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 35.2 +++ b/src/fontdraw.iwram.c Fri Mar 13 00:39:12 2009 -0700 35.3 @@ -0,0 +1,1 @@ 35.4 +#include "fontdraw_engine.c"
36.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 36.2 +++ b/src/fontdraw_engine.c Fri Mar 13 00:39:12 2009 -0700 36.3 @@ -0,0 +1,135 @@ 36.4 +/* 36.5 +Variable width font drawing library for DS (and GBA) 36.6 + 36.7 +Copyright 2007-2008 Damian Yerrick <pinoandchester@pineight.com> 36.8 + 36.9 +This work is provided 'as-is', without any express or implied 36.10 +warranty. In no event will the authors be held liable for any 36.11 +damages arising from the use of this work. 36.12 + 36.13 +Permission is granted to anyone to use this work for any purpose, 36.14 +including commercial applications, and to alter it and redistribute 36.15 +it freely, subject to the following restrictions: 36.16 + 36.17 +1. The origin of this work must not be misrepresented; you must 36.18 + not claim that you wrote the original work. If you use 36.19 + this work in a product, an acknowledgment in the product 36.20 + documentation would be appreciated but is not required. 36.21 +2. Altered source versions must be plainly marked as such, and must 36.22 + not be misrepresented as being the original work. 36.23 +3. This notice may not be removed or altered from any source 36.24 + distribution. 36.25 + 36.26 +"Source" is the preferred form of a work for making changes to it. 36.27 + 36.28 +*/ 36.29 +/* 36.30 + 36.31 +Set FONTDRAW_SPLIT_COMPILE when compiling the time-sensitve parts 36.32 +into a separate file to be placed in "fast" memory. 36.33 + 36.34 +*/ 36.35 + 36.36 + 36.37 +#ifdef FONTDRAW_SPLIT_COMPILE 36.38 +typedef unsigned int u32; 36.39 +extern const unsigned char vwfont_bin[]; 36.40 + 36.41 +typedef struct GlyphRec { 36.42 + unsigned short dataOffset; 36.43 + unsigned char glyphWidth; 36.44 + unsigned char reserved; 36.45 +} GlyphRec; 36.46 +#endif 36.47 + 36.48 +unsigned int fontdraw_putchar(u32 *dst, unsigned int colStride, int wid, int x, int glyph) { 36.49 + glyph &= 0xFF; 36.50 + if (glyph < vwfont_bin[1]) { 36.51 + return 0; 36.52 + } 36.53 + glyph -= vwfont_bin[1]; 36.54 + if (vwfont_bin[2] != 0 && glyph >= vwfont_bin[2]) { 36.55 + return 0; 36.56 + } 36.57 + const GlyphRec *glyphRec = 36.58 + ((const GlyphRec *)(vwfont_bin + vwfont_bin[0])) + glyph; 36.59 + const unsigned char *data = vwfont_bin + glyphRec->dataOffset; 36.60 + unsigned int dataShift = 2; 36.61 + unsigned int dataBits = *data++; 36.62 + unsigned int pixelCode = dataBits & 0x03; 36.63 + 36.64 + // Convert x to tile column address and bit address within tile 36.65 + dst += colStride * (x >> 3); 36.66 + x = (x & 0x07) << 2; 36.67 + 36.68 + if (dataShift >= 8) { 36.69 + dataShift = 2; 36.70 + dataBits = *data++; 36.71 + } 36.72 + 36.73 + for (unsigned int height = vwfont_bin[3]; 36.74 + height > 0; 36.75 + --height, ++dst) { 36.76 + int eol = 0; 36.77 + int xLine = x; 36.78 + int widLeft = wid; 36.79 + u32 *dstLine = dst; 36.80 + u32 dstBits = *dstLine; 36.81 + while (!eol) { 36.82 + 36.83 + // Process a pixel instruction 36.84 + if (pixelCode == 0) { 36.85 + eol = 1; 36.86 + } else if (widLeft > 0) { 36.87 + 36.88 + // Change pixel and move to next pixel 36.89 + if (pixelCode > 1) { 36.90 + //dstBits &= ~(0x0F << xLine); 36.91 + dstBits |= (pixelCode - 1) << xLine; 36.92 + } 36.93 + xLine += 4; 36.94 + --widLeft; 36.95 + } 36.96 + 36.97 + // If finished with this tile, write back changed bits 36.98 + if (xLine >= 32) { 36.99 + xLine = 0; 36.100 + *dstLine = dstBits; 36.101 + dstLine += colStride; 36.102 + dstBits = *dstLine; 36.103 + } 36.104 + 36.105 + // Decode next pixel instruction 36.106 + if (dataShift >= 8) { 36.107 + dataShift = 0; 36.108 + dataBits = *data++; 36.109 + } 36.110 + 36.111 + // Decode a byte into pixel instructions 36.112 + pixelCode = (dataBits >> dataShift) & 0x03; 36.113 + dataShift += 2; 36.114 + } 36.115 + 36.116 + // Write back changed bits 36.117 + if (xLine > 0) { 36.118 + *dstLine = dstBits; 36.119 + } 36.120 + } 36.121 + return glyphRec->glyphWidth; 36.122 +} 36.123 + 36.124 +void vwfRectfillColumn(u32 *dst, unsigned int colStride, 36.125 + unsigned int l, unsigned int t, 36.126 + unsigned int r, unsigned int b, 36.127 + unsigned int c) 36.128 +{ 36.129 + u32 mask = 0xffffffffU << (4 * l); 36.130 + mask &= 0xffffffffU >> (4 * (8 - r)); 36.131 + c &= mask; 36.132 + mask = ~mask; 36.133 + 36.134 + for (; t < b; t++) { 36.135 + dst[t] = (dst[t] & mask) | c; 36.136 + } 36.137 +} 36.138 +
37.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 37.2 +++ b/src/gba_asm.s Fri Mar 13 00:39:12 2009 -0700 37.3 @@ -0,0 +1,73 @@ 37.4 +@ fixed fastfmul(fixed x, fixed y) 37.5 +@ Multiply two 16.16 fixed-point numbers. 37.6 + 37.7 +.ARM 37.8 +.ALIGN 37.9 +.GLOBL fastfmul 37.10 + 37.11 +fastfmul: 37.12 + smull r1,r2,r0,r1 37.13 + mov r0,r2,LSL#16 37.14 + orr r0,r0,r1,LSR#16 37.15 + bx lr 37.16 + 37.17 + 37.18 +@ int dv(int num, int den) 37.19 +@ Divide two signed integers. 37.20 + 37.21 +.THUMB 37.22 +.THUMB_FUNC 37.23 +.ALIGN 37.24 +.GLOBL dv 37.25 + 37.26 +dv: 37.27 + cmp r1, #0 37.28 + beq 0f 37.29 + swi 6 37.30 + bx lr 37.31 +0: 37.32 + ldr r0, =0x7fffffff 37.33 + bx lr 37.34 + 37.35 + 37.36 +@ int fracmul(signed int x, signed int frac) 37.37 +@ Multiply by a 0.32 fractional number between -0.5 and 0.5. 37.38 +@ Used for fast division by a constant. 37.39 + 37.40 +.ARM 37.41 +.ALIGN 37.42 +.GLOBL fracmul 37.43 + 37.44 +fracmul: 37.45 + smull r1,r2,r0,r1 37.46 + mov r0, r2 37.47 + bx lr 37.48 + 37.49 + 37.50 +@ int fracumul(unsigned int x, unsigned int frac) 37.51 +@ Multiply by a 0.32 fractional number between 0 and 1. 37.52 +@ Used for fast division by a constant. 37.53 + 37.54 +.ARM 37.55 +.ALIGN 37.56 +.GLOBL fracumul 37.57 + 37.58 +fracumul: 37.59 + umull r1,r2,r0,r1 37.60 + mov r0, r2 37.61 + bx lr 37.62 + 37.63 + 37.64 +@ void gblz_unpack(const void *src, void *dst) 37.65 +@ Unpack GB LZSS format data. 37.66 + 37.67 +.THUMB 37.68 +.THUMB_FUNC 37.69 +.ALIGN 37.70 +.GLOBL _gblz_unpack 37.71 + 37.72 +_gblz_unpack: 37.73 + swi 0x11 @ LZ77UnCompWRAM 37.74 + bx lr 37.75 + 37.76 +
38.1 Binary file src/gbablk.chr has changed
39.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 39.2 +++ b/src/gbaisr.iwram.c Fri Mar 13 00:39:12 2009 -0700 39.3 @@ -0,0 +1,15 @@ 39.4 +#include "ljgba.h" 39.5 +#define BIOS_IF (*(volatile unsigned short *)0x03FFFFF8) 39.6 + 39.7 +volatile int curTime = 0; 39.8 + 39.9 +void isr(void) { 39.10 + unsigned int interrupts = REG_IF; 39.11 + 39.12 + if (interrupts & IRQ_VBLANK) { 39.13 + ++curTime; 39.14 + } 39.15 + 39.16 + BIOS_IF |= interrupts; 39.17 + REG_IF = interrupts; 39.18 +}
40.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 40.2 +++ b/src/gbamenus.c Fri Mar 13 00:39:12 2009 -0700 40.3 @@ -0,0 +1,458 @@ 40.4 +/* 40.5 + 40.6 +BGCTRL layout 40.7 +Layer 0: Current menu 40.8 +Layer 1: Menu that is fading away 40.9 +Layer 2: Gradient background 40.10 + 40.11 +VRAM layout 40.12 +(1 map == 64 tiles) 40.13 +Tiles 0.000-2FF: layer 0 bitmap 40.14 +Tiles 0.300-3FF: unused 40.15 +Tiles 2.000-2FF: layer 1 bitmap 40.16 +Tiles 2.300-33F: unused 40.17 +Tiles 2.340-36F: map 29 (layer 2) 40.18 +Tiles 2.370-37F: layer 2 gradient tiles 40.19 +Tiles 2.380-3FF: layer 0 map 40.20 +Tiles 2.3C0-3FF: layer 1 map 40.21 + 40.22 + 40.23 +*/ 40.24 + 40.25 +#include <stdint.h> 40.26 +#include <string.h> 40.27 +#include "fontdraw.h" 40.28 +typedef u32 TileSliver; 40.29 + 40.30 +#ifdef ARM9 40.31 +#include <nds.h> 40.32 +#define MENU_GFX_CORE 1 40.33 +#define MENU_GFX_VRAM(bank, tile) (TileSliver *)(BG_TILE_RAM_SUB(bank) + 32 * tile) 40.34 +#define MENU_GFX_MAP ((NAMETABLE *)0x06200000) 40.35 +#define MENU_GFX_BGCTRL BGCTRL_SUB 40.36 +#define MENU_GFX_OFFSET ((bg_scroll *)(0x04001010)) 40.37 +#define HIDDEN_ROWS 0 40.38 +#define HIDDEN_COLS 0 40.39 +#define BG0_ON DISPLAY_BG0_ACTIVE 40.40 +#define BG1_ON DISPLAY_BG1_ACTIVE 40.41 +#define BG2_ON DISPLAY_BG2_ACTIVE 40.42 +#define videoSetModeMenu(x) videoSetModeSub(x) 40.43 +#define MENU_GFX_PALETTE BG_PALETTE_SUB 40.44 +#define USING_TOUCH 1 40.45 +#else 40.46 +#include <gba.h> 40.47 +#define MENU_GFX_CORE 0 40.48 +#define MENU_GFX_VRAM(bank, tile) (TileSliver *)PATRAM4(bank, tile) 40.49 +#define MENU_GFX_MAP MAP 40.50 +#define MENU_GFX_BGCTRL BGCTRL 40.51 +#define MENU_GFX_OFFSET BG_OFFSET 40.52 +#define HIDDEN_ROWS 2 40.53 +#define HIDDEN_COLS 1 40.54 +#define videoSetModeMenu(x) (REG_DISPCNT = x) 40.55 +#define MODE_0_2D 0 40.56 +#define MENU_GFX_PALETTE BG_PALETTE 40.57 +#define USING_TOUCH 0 40.58 +#endif 40.59 + 40.60 +static const VWFWindow vwfLayer0 = { 40.61 + .left = 0, .top = 0, .width = 32, .height = 24, 40.62 + .chrBase = MENU_GFX_VRAM(0, 0), 40.63 + .map = 30, 40.64 + .core = MENU_GFX_CORE, 40.65 + .mapTileBase = 0 40.66 +}; 40.67 + 40.68 +static const VWFWindow vwfLayer1 = { 40.69 + .left = 0, .top = 0, .width = 32, .height = 24, 40.70 + .chrBase = MENU_GFX_VRAM(2, 0), 40.71 + .map = 31, 40.72 + .core = MENU_GFX_CORE, 40.73 + .mapTileBase = 0 40.74 +}; 40.75 + 40.76 + 40.77 +void vsync(void); 40.78 + 40.79 +static const TileSliver gradientBackgroundDelta[8] = { 40.80 + 0x00000000, 40.81 + 0x10101010, 40.82 + 0x00000000, 40.83 + 0x01010101, 40.84 + 0x10101010, 40.85 + 0x01010101, 40.86 + 0x11111111, 40.87 + 0x10101010 40.88 +}; 40.89 + 40.90 +/* Makes 12 gradient background tiles starting at dst. 40.91 +*/ 40.92 + 40.93 +static void makeGradientBackgroundTiles(TileSliver *dst) { 40.94 + for (TileSliver tile = 0x11111111; 40.95 + tile < 0xDDDDDDDD; 40.96 + tile += 0x11111111) { 40.97 + for (unsigned int y = 0; y < 8; ++y) { 40.98 + *dst++ = tile + gradientBackgroundDelta[y]; 40.99 + } 40.100 + } 40.101 +} 40.102 + 40.103 +static void makeLayer2(void) { 40.104 + makeGradientBackgroundTiles(MENU_GFX_VRAM(2, 0x300)); 40.105 + for (int y = 0; y < 12; ++y) { 40.106 + for (int x = 0; x < 32; ++x) { 40.107 + MENU_GFX_MAP[29][y][x] = 0xE300 + y; 40.108 + } 40.109 + } 40.110 + for (int y = 0; y < 12; ++y) { 40.111 + for (int x = 0; x < 32; ++x) { 40.112 + MENU_GFX_MAP[29][y + 12][x] = 0xF300 + y; 40.113 + } 40.114 + } 40.115 + MENU_GFX_BGCTRL[2] = BG_TILE_BASE(2) | BG_MAP_BASE(29); 40.116 + MENU_GFX_OFFSET[2].x = HIDDEN_COLS * 8; 40.117 + MENU_GFX_OFFSET[2].y = HIDDEN_ROWS * 8; 40.118 +} 40.119 + 40.120 + 40.121 + 40.122 +/* 40.123 +Button palette: 40.124 +0 transparent 40.125 +1 White (button top) 40.126 +2 Medium gray (button side) 40.127 +3 Darkest gray (button bottom) 40.128 +4 Light gray (button background) 40.129 +5 Dark gray (button text aa, lower corner) 40.130 +6 Black (button text) 40.131 +7 Black 40.132 + 40.133 +This comes in both ordinary and highlighted versions. There are two copies of the highlighted text. 40.134 + 40.135 +*/ 40.136 + 40.137 +static const TileSliver buttonSideTiles[16] = { 40.138 + 0x11111114, 40.139 + 0x11111142, 40.140 + 0x11111422, 40.141 + 0x44444222, 40.142 + 0x44444222, 40.143 + 0x44444222, 40.144 + 0x44444222, 40.145 + 0x44444222, 40.146 + 0x44444222, 40.147 + 0x44444222, 40.148 + 0x44444222, 40.149 + 0x44444222, 40.150 + 0x44444222, 40.151 + 0x33333522, 40.152 + 0x33333352, 40.153 + 0x33333335 40.154 +}; 40.155 + 40.156 +static void loadButtonSideTiles(void) { 40.157 + for (int bank = 0; bank <= 2; bank += 2) { 40.158 + TileSliver *dst = MENU_GFX_VRAM(bank, 0x30C); 40.159 + memcpy(dst, buttonSideTiles, 32); 40.160 + memcpy(dst + 8, buttonSideTiles + 4, 32); 40.161 + memcpy(dst + 16, buttonSideTiles + 8, 32); 40.162 + } 40.163 +} 40.164 + 40.165 +static const u8 buttonIntensity[8] = 40.166 + {28, 31, 23, 15, 28, 19, 0, 0}; 40.167 + 40.168 +/** 40.169 + * Loads the unhighlighted (gray) button palette. 40.170 + */ 40.171 +static void loadButtonPalette(void) { 40.172 + for (int i = 0; i < 8; ++i) { 40.173 + unsigned int intensity = buttonIntensity[i]; 40.174 + 40.175 + MENU_GFX_PALETTE[192 + i] = intensity * RGB5(1, 1, 1); 40.176 + } 40.177 +} 40.178 + 40.179 +void ljmenu_setHilitePalette(int phase) { 40.180 + 40.181 + // generate triangle wave 40.182 + phase = (phase & 0x3F) ^ 0x20; 40.183 + if (phase & 0x20) { 40.184 + phase ^= 0x3F; 40.185 + } 40.186 + 40.187 + for (int i = 0; i < 8; ++i) { 40.188 + int intensity = buttonIntensity[i]; 40.189 + int intensity34 = (3 * intensity) >> 2; 40.190 + int rg = intensity34 + (phase >> 2) + 4; 40.191 + int b = rg >> 1; 40.192 + if (rg > 31) { 40.193 + rg = 31; 40.194 + } 40.195 + unsigned int c = RGB5(1, 1, 0) * rg + RGB5(0, 0, 1) * b; 40.196 + 40.197 + MENU_GFX_PALETTE[200 + i] = c; 40.198 + MENU_GFX_PALETTE[208 + i] = c; 40.199 + } 40.200 +} 40.201 + 40.202 +#define TILE_HFLIP 0x0400 40.203 + 40.204 +void ljmenu_hiliteButton(int l, int t, int r, int b, int hilite) { 40.205 + hilite = hilite ? 0xD000 : 0xC000; 40.206 + 40.207 + /* Draw sides of button */ 40.208 + MENU_GFX_MAP[30][t][l] = 0x30C | hilite; 40.209 + MENU_GFX_MAP[30][t][r - 1] = 0x30C | TILE_HFLIP | hilite; 40.210 + for (int y = t + 1; y < b - 1; ++y) { 40.211 + MENU_GFX_MAP[30][y][l] = 0x30D | hilite; 40.212 + MENU_GFX_MAP[30][y][r - 1] = 0x30D | TILE_HFLIP | hilite; 40.213 + } 40.214 + MENU_GFX_MAP[30][b - 1][l] = 0x30E | hilite; 40.215 + MENU_GFX_MAP[30][b - 1][r - 1] = 0x30E | TILE_HFLIP | hilite; 40.216 + vwfPutMap(&vwfLayer0, l + 1, t, r - 1, b, hilite); 40.217 +} 40.218 + 40.219 +void ljmenu_drawButton(int l, int t, int r, int b, const char *text) { 40.220 + vwfRectfill(&vwfLayer0, 40.221 + l * 8 + 8, t * 8, r * 8 - 8, t * 8 + 3, 1); 40.222 + vwfRectfill(&vwfLayer0, 40.223 + l * 8 + 8, t * 8 + 3, r * 8 - 8, b * 8 - 3, 4); 40.224 + vwfRectfill(&vwfLayer0, 40.225 + l * 8 + 8, b * 8 - 3, r * 8 - 8, b * 8, 3); 40.226 + 40.227 + int w = fontdraw_strWidth(text); 40.228 + int x = l * 8 + (r - l) * 4 - w / 2; 40.229 + int y = t * 8 + (b - t) * 4 - 12 / 2; 40.230 + 40.231 + vwfPuts(&vwfLayer0, text, x, y); 40.232 + ljmenu_hiliteButton(l, t, r, b, 0); 40.233 +} 40.234 + 40.235 +static void makePalettes(void) { 40.236 + // make background layer palettes 40.237 + for (unsigned int i = 0; i <= 12; ++i) { 40.238 + int blue = 2 * i; 40.239 + MENU_GFX_PALETTE[225 + i]= RGB5(0, 0, blue); 40.240 + } 40.241 + for (unsigned int i = 0; i <= 12; ++i) { 40.242 + int red = ((i + 9) * 3) >> 1; 40.243 + MENU_GFX_PALETTE[241 + i]= RGB5(red, red / 2, 0); 40.244 + } 40.245 + 40.246 + MENU_GFX_PALETTE[1] = RGB5(21,21,23); 40.247 + MENU_GFX_PALETTE[2] = RGB5(31,31,31); 40.248 + MENU_GFX_PALETTE[3] = RGB5(31,31,31); 40.249 + loadButtonPalette(); 40.250 +} 40.251 + 40.252 +void ljmenu_cls(void) { 40.253 + vwfWinInit(&vwfLayer0); 40.254 + vwfPutMap(&vwfLayer0, 2, 4, 30, 22, 0xC000); 40.255 +} 40.256 + 40.257 +void ljmenu_init(void) { 40.258 + makeLayer2(); 40.259 + loadButtonSideTiles(); 40.260 + makePalettes(); 40.261 + ljmenu_cls(); 40.262 + MENU_GFX_BGCTRL[0] = BG_TILE_BASE(0) | BG_MAP_BASE(30); 40.263 + MENU_GFX_OFFSET[0].x = HIDDEN_COLS * 8; 40.264 + MENU_GFX_OFFSET[0].y = HIDDEN_ROWS * 8; 40.265 + MENU_GFX_BGCTRL[1] = BG_TILE_BASE(2) | BG_MAP_BASE(31); 40.266 + MENU_GFX_OFFSET[1].x = HIDDEN_COLS * 8; 40.267 + MENU_GFX_OFFSET[1].y = HIDDEN_ROWS * 8; 40.268 + videoSetModeMenu(MODE_0_2D | BG0_ON | BG1_ON | BG2_ON); 40.269 +} 40.270 + 40.271 +void ljmenu_freeze(void) { 40.272 + 40.273 + // Copy background 40.274 + memcpy(vwfLayer0.chrBase, vwfLayer1.chrBase, 240*160/2); 40.275 + 40.276 + // Copy map 40.277 + memcpy(MENU_GFX_MAP[30], MENU_GFX_MAP[31], sizeof(NAMETABLE)); 40.278 +} 40.279 + 40.280 +void ljmenu_setTitle(const char *topLeft, const char *topRight) { 40.281 + vwfRectfill(&vwfLayer0, 16, 16, 240, 16 + 12, 0); 40.282 + vwfPuts(&vwfLayer0, topLeft, 16, 16); 40.283 + if (topRight) { 40.284 + int x = 240 - fontdraw_strWidth(topRight); 40.285 + vwfPuts(&vwfLayer0, topRight, x, 16); 40.286 + } 40.287 +} 40.288 + 40.289 +static unsigned short tabsX; 40.290 +static unsigned short tabsPadding; 40.291 +#define TAB_TOP 32 40.292 +#define TAB_BOTTOM 44 40.293 +#define TAB_LEFT 16 40.294 +#define TAB_RIGHT 240 40.295 + 40.296 +void ljmenu_beginTabs(unsigned int padding) { 40.297 + tabsX = TAB_LEFT; 40.298 + tabsPadding = padding; 40.299 +} 40.300 + 40.301 +void ljmenu_addTab(const char *text, int hilite) { 40.302 + unsigned int bgColor = hilite ? 12 : 4; 40.303 + unsigned int w = text ? fontdraw_strWidth(text) : 0; 40.304 + unsigned int left = tabsX; 40.305 + unsigned int right = left + 2 * tabsPadding + w; 40.306 + 40.307 + if (right > TAB_RIGHT) { 40.308 + return; 40.309 + } 40.310 + vwfRectfill(&vwfLayer0, 40.311 + left, TAB_TOP, right, TAB_BOTTOM, 40.312 + bgColor); 40.313 + if (hilite) { 40.314 + vwfHline(&vwfLayer0, 40.315 + left, TAB_TOP, right, 40.316 + bgColor + 1); 40.317 + vwfVline(&vwfLayer0, 40.318 + left, TAB_TOP, TAB_BOTTOM, 40.319 + bgColor + 1); 40.320 + vwfVline(&vwfLayer0, 40.321 + right - 1, TAB_TOP, TAB_BOTTOM, 40.322 + bgColor + 1); 40.323 + } else { 40.324 + vwfHline(&vwfLayer0, 40.325 + left, TAB_BOTTOM - 1, right, 40.326 + bgColor + 1); 40.327 + } 40.328 + if (text) { 40.329 + vwfPuts(&vwfLayer0, text, left + tabsPadding, TAB_TOP); 40.330 + } 40.331 + tabsX = right; 40.332 +} 40.333 + 40.334 +void ljmenu_endTabs(void) { 40.335 + vwfRectfill(&vwfLayer0, 40.336 + tabsX, TAB_TOP, TAB_RIGHT, TAB_BOTTOM - 1, 40.337 + 4); 40.338 + vwfHline(&vwfLayer0, 40.339 + tabsX, TAB_BOTTOM - 1, TAB_RIGHT, 40.340 + 5); 40.341 +} 40.342 + 40.343 +#define PROPPANEL_TOP TAB_BOTTOM 40.344 +#define PROPPANEL_HT 12 40.345 +#define PROPPANEL_ROWS 7 40.346 +#define PROPPANEL_BOTTOM (PROPPANEL_TOP + PROPPANEL_HT * PROPPANEL_ROWS) 40.347 + 40.348 +void ljmenu_propPanelClear(unsigned int nRows) { 40.349 + if (nRows > PROPPANEL_ROWS) { 40.350 + nRows = PROPPANEL_ROWS; 40.351 + } 40.352 + int y = PROPPANEL_TOP + PROPPANEL_HT * nRows; 40.353 + vwfRectfill(&vwfLayer0, 40.354 + TAB_LEFT, PROPPANEL_TOP, TAB_RIGHT, y, 40.355 + 4); 40.356 + vwfRectfill(&vwfLayer0, 40.357 + TAB_LEFT, y, TAB_RIGHT, PROPPANEL_BOTTOM, 40.358 + 0); 40.359 +} 40.360 + 40.361 +void ljmenu_propPanelDrawRow(const char *name, const char *value, 40.362 + unsigned int y, unsigned int hilite) { 40.363 + if (y > PROPPANEL_ROWS) { 40.364 + return; 40.365 + } 40.366 + y = PROPPANEL_TOP + PROPPANEL_HT * y; 40.367 + unsigned int bgColor = hilite ? 12 : 4; 40.368 + 40.369 + vwfRectfill(&vwfLayer0, 40.370 + TAB_LEFT, y, TAB_RIGHT, y + PROPPANEL_HT, 40.371 + bgColor); 40.372 + if (hilite) { 40.373 + vwfRect(&vwfLayer0, 40.374 + TAB_LEFT, y, TAB_RIGHT, y + PROPPANEL_HT, 40.375 + bgColor + 1); 40.376 + } 40.377 + if (name) { 40.378 + vwfPuts(&vwfLayer0, name, TAB_LEFT + 8, y); 40.379 + } 40.380 + if (value) { 40.381 + int x = TAB_RIGHT - 8 - fontdraw_strWidth(value); 40.382 + vwfPuts(&vwfLayer0, value, x, y); 40.383 + } 40.384 +} 40.385 + 40.386 +static void roundrect(const VWFWindow *w, 40.387 + int l, int t, int r, int b, int c) { 40.388 + vwfRectfill(w, l, t + 2, r, b - 2, c); 40.389 + vwfHline(w, l + 2, t, r - 2, c); 40.390 + vwfHline(w, l + 1, t + 1, r - 1, c); 40.391 + vwfHline(w, l + 1, b - 2, r - 1, c); 40.392 + vwfHline(w, l + 2, b - 1, r - 2, c); 40.393 +} 40.394 + 40.395 +static void ljmenu_balloon(const char *text1, const char *text2, 40.396 + int l, int t, int r) { 40.397 + int b = t + (text2 ? 24 : 12); 40.398 + roundrect(&vwfLayer0, l, t, r, b, 4); 40.399 + vwfPuts(&vwfLayer0, text1, l + 4, t); 40.400 + if (text2) { 40.401 + vwfPuts(&vwfLayer0, text2, l + 4, t + 12); 40.402 + } 40.403 +} 40.404 + 40.405 +void ljmenu_propPanelDrawDesc(const char *text1, const char *text2) { 40.406 + ljmenu_balloon(text1, text2, TAB_LEFT, PROPPANEL_BOTTOM + 4, TAB_RIGHT); 40.407 +} 40.408 + 40.409 +void ljmenu_propPanelDrawHelp(const char *text1, const char *text2) { 40.410 + int y = text2 ? 176 - 24 : 176 - 12; 40.411 + ljmenu_balloon(text1, text2, TAB_LEFT, y, TAB_RIGHT); 40.412 +} 40.413 + 40.414 +#if 0 40.415 +static void ljmenu_pressA(void) { 40.416 + int phase = 0; 40.417 + while (!(REG_KEYINPUT & KEY_A)) { 40.418 + ++phase; 40.419 + vsync(); 40.420 + ljmenu_setHilitePalette(phase); 40.421 + } 40.422 + while (REG_KEYINPUT & KEY_A) { 40.423 + ++phase; 40.424 + vsync(); 40.425 + ljmenu_setHilitePalette(phase); 40.426 + } 40.427 +} 40.428 + 40.429 +void ljmenuTest(void) { 40.430 + ljmenu_init(); 40.431 + ljmenu_cls(); 40.432 + ljmenu_setTitle("LOCKJAW 0.43", "© 2008 Damian Yerrick"); 40.433 + ljmenu_drawButton(22, 18, 22 + 8, 18 + 3, "OK"); 40.434 + ljmenu_hiliteButton(22, 18, 22 + 8, 18 + 3, 1); 40.435 + ljmenu_drawButton(13, 18, 13 + 8, 18 + 3, "Cancel"); 40.436 + ljmenu_drawButton(2, 8, 30, 11, 40.437 + "coming soon: the new look of LOCKJAW"); 40.438 + ljmenu_pressA(); 40.439 + 40.440 + ljmenu_cls(); 40.441 + ljmenu_setTitle("LOCKJAW 0.43", "© 2008 Damian Yerrick"); 40.442 + ljmenu_beginTabs(4); 40.443 + ljmenu_addTab("Game", 1); 40.444 + ljmenu_addTab("Well", 0); 40.445 + ljmenu_addTab("Move", 0); 40.446 + ljmenu_addTab("Line", 0); 40.447 + ljmenu_addTab("Ctrl", 0); 40.448 + ljmenu_addTab("Drop", 0); 40.449 + ljmenu_addTab("Disp", 0); 40.450 + ljmenu_endTabs(); 40.451 + ljmenu_propPanelClear(7); 40.452 + ljmenu_propPanelDrawRow("Gimmick", "Marathon", 0, 1); 40.453 + ljmenu_propPanelDrawRow("Mr. Gimmick", "Halo", 1, 0); 40.454 + ljmenu_propPanelDrawDesc("Goal or other game mode", 40.455 + "Play until you <Ganon>DIE.</Ganon>"); 40.456 + ljmenu_propPanelDrawHelp("u d: move; l r: change; L R: page; Start: OK", 40.457 + NULL); 40.458 + ljmenu_pressA(); 40.459 +} 40.460 + 40.461 +#endif
41.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 41.2 +++ b/src/gbamenus.h Fri Mar 13 00:39:12 2009 -0700 41.3 @@ -0,0 +1,26 @@ 41.4 +#ifndef GBAMENUS_H 41.5 +#define GBAMENUS_H 41.6 + 41.7 +void ljmenu_init(void); 41.8 +void ljmenu_cls(void); 41.9 +void ljmenu_setTitle(const char *topLeft, const char *topRight); 41.10 +void ljmenu_freeze(void); 41.11 + 41.12 +void ljmenu_drawButton(int l, int t, int r, int b, const char *text); 41.13 +void ljmenu_hiliteButton(int l, int t, int r, int b, int hilite); 41.14 +void ljmenu_setHilitePalette(int phase); 41.15 + 41.16 +void ljmenu_beginTabs(unsigned int padding); 41.17 +void ljmenu_addTab(const char *text, int hilite); 41.18 +void ljmenu_endTabs(void); 41.19 + 41.20 +void ljmenu_propPanelClear(unsigned int nRows); 41.21 +void ljmenu_propPanelDrawRow(const char *name, const char *value, 41.22 + unsigned int y, unsigned int hilite); 41.23 +void ljmenu_propPanelDrawDesc(const char *text1, const char *text2); 41.24 +void ljmenu_propPanelDrawHelp(const char *text1, const char *text2); 41.25 + 41.26 + 41.27 +void ljmenuTest(void); 41.28 + 41.29 +#endif
42.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 42.2 +++ b/src/gbanotefreq.c Fri Mar 13 00:39:12 2009 -0700 42.3 @@ -0,0 +1,25 @@ 42.4 +/* notefreq.h generated by mkpowers.c 42.5 + lookup table for note frequencies */ 42.6 + 42.7 +/* DMG Sound period values 42.8 + frequency = 131072 Hz/(2048 + n) */ 42.9 +const unsigned short note_freqs[64] = { 42.10 + 44, 156, 262, 362, 457, 546, 630, 710, 785, 856, 923, 986, 42.11 + 1046, 1102, 1155, 1205, 1252, 1297, 1339, 1379, 1416, 1452, 1485, 1517, 42.12 + 1547, 1575, 1601, 1626, 1650, 1672, 1693, 1713, 1732, 1750, 1766, 1782, 42.13 + 1797, 1811, 1824, 1837, 1849, 1860, 1870, 1880, 1890, 1899, 1907, 1915, 42.14 + 1922, 1929, 1936, 1942, 1948, 1954, 1959, 1964, 1969, 1973, 1977, 1981, 42.15 + 1985, 1988, 1992, 1995 42.16 +}; 42.17 +#if 0 42.18 +/* DMG Sound period values 42.19 + frequency = base_freq * n / 4096 */ 42.20 +const unsigned short sampled_freqs[64] = { 42.21 + 1024, 1084, 1149, 1217, 1290, 1366, 1448, 1534, 1625, 1722, 1824, 1933, 42.22 + 2048, 2169, 2298, 2435, 2580, 2733, 2896, 3068, 3250, 3444, 3649, 3866, 42.23 + 4096, 4339, 4597, 4870, 5160, 5467, 5792, 6137, 6501, 6888, 7298, 7732, 42.24 + 8192, 8679, 9195, 9741,10321,10935,11585,12274,13003,13777,14596,15464, 42.25 +16384,17358,18390,19483,20642,21870,23170,24548,26007,27554,29192,30928, 42.26 +32768,34716,36780,38967 42.27 +}; 42.28 +#endif 42.29 \ No newline at end of file
43.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 43.2 +++ b/src/gbaopt.c Fri Mar 13 00:39:12 2009 -0700 43.3 @@ -0,0 +1,123 @@ 43.4 +/* 43.5 + 43.6 +options for gba port of lockjaw 43.7 + 43.8 +*/ 43.9 + 43.10 +#include <stdio.h> 43.11 +#include <string.h> 43.12 +#include "options.h" 43.13 +#include "ljlocale.h" 43.14 +#include "ljplay.h" 43.15 +#include "gbamenus.h" 43.16 + 43.17 +#ifdef ARM9 43.18 +#include "ljds.h" 43.19 +#else 43.20 +#include "ljgba.h" 43.21 +#endif 43.22 + 43.23 +#define optionsMenu commonOptionsMenu 43.24 + 43.25 +unsigned char customPrefs[OPTIONS_MENU_LEN]; 43.26 + 43.27 +void unpackOptions(LJView *v, const unsigned char *prefs) { 43.28 + unpackCommonOptions(v, prefs); 43.29 +} 43.30 + 43.31 +static const char optionsHelpText[] = 43.32 +"\020\021 page \026\027 move \025\024 change Start:play"; 43.33 + 43.34 +void optionsWinInit(void) { 43.35 + ljmenu_init(); 43.36 + ljmenu_cls(); 43.37 + ljmenu_propPanelDrawHelp(optionsHelpText, 43.38 + NULL); 43.39 +} 43.40 + 43.41 +static void optionsClearRow(int y, int hilite) { 43.42 + ljmenu_propPanelDrawRow("", "", y, hilite); 43.43 +} 43.44 + 43.45 +static char hiliteBlinkPhase = 1; 43.46 + 43.47 +void optionsDrawRow(const unsigned char *prefs, 43.48 + int y, int line, int value, int hilite) { 43.49 + char txt[OPTIONS_VALUE_LEN]; 43.50 + const char *nameText; 43.51 + const char *valueDesc = NULL; 43.52 + char altNameText[8]; 43.53 + const char *valueOverride = isDisabledOption(prefs, line); 43.54 + 43.55 + { 43.56 + nameText = ljGetFourCCName(optionsMenu[line].name); 43.57 + if (!nameText) { 43.58 + strncpy(altNameText, optionsMenu[line].name.c, 4); 43.59 + altNameText[4] = 0; 43.60 + nameText = altNameText; 43.61 + } 43.62 + } 43.63 + 43.64 + // Format value 43.65 + if (valueOverride) { 43.66 + strcpy(txt, "overridden"); 43.67 + valueDesc = valueOverride; 43.68 + } else { 43.69 + valueDesc = getOptionsValueStr(txt, line, value); 43.70 + } 43.71 + 43.72 + ljmenu_propPanelDrawRow(nameText, txt, y, hilite); 43.73 + 43.74 + if (hilite & 1) { 43.75 + hiliteBlinkPhase = 0; 43.76 + const char *descText = ljGetFourCCDesc(optionsMenu[line].name); 43.77 + ljmenu_propPanelDrawDesc(descText ? descText : "", 43.78 + valueDesc ? valueDesc : ""); 43.79 + } 43.80 +} 43.81 + 43.82 +static const char *const optionsPageShortNames[7] = { 43.83 + "Game", "Well", "Move", "Line", "Ctrl", "Drop", "Disp" 43.84 +}; 43.85 + 43.86 +void optionsDrawPage(int page, const unsigned char *prefs) { 43.87 + int nPages = 0; 43.88 + for (; optionsPages[nPages].name; ++nPages) { } 43.89 + 43.90 + // draw page title 43.91 + ljmenu_setTitle("LOCKJAW > Options", optionsPages[page].name); 43.92 + 43.93 + // draw all tabs 43.94 + ljmenu_beginTabs(4); 43.95 + for (int p = 0; p < 7; ++p) { 43.96 + ljmenu_addTab(optionsPageShortNames[p], p == page); 43.97 + } 43.98 + ljmenu_endTabs(); 43.99 + 43.100 + for (int i = optionsPages[page].start; 43.101 + i < optionsPages[page + 1].start; ++i) { 43.102 + optionsDrawRow(prefs, i - optionsPages[page].start, 43.103 + i, prefs[i], 0); 43.104 + } 43.105 + for (int y = optionsPages[page + 1].start - optionsPages[page].start; 43.106 + y < 7; ++y) { 43.107 + optionsClearRow(y, 0); 43.108 + } 43.109 +} 43.110 + 43.111 +void optionsIdle(void) { 43.112 + vsync(); 43.113 + ljmenu_setHilitePalette(++hiliteBlinkPhase); 43.114 +} 43.115 + 43.116 +#if 0 43.117 +typedef u16 hicolor_t; 43.118 + 43.119 +__attribute__((aligned(4))) 43.120 +const hicolor_t gbaOptionsPalette[8] = 43.121 +{ 43.122 + RGB5(31,31,31), RGB5(20,20,20), RGB5( 0, 0, 0), RGB5( 0, 0, 0), 43.123 + RGB5(31,31,23), RGB5(20,20,15), RGB5( 0, 0, 0), RGB5( 0, 0, 0) 43.124 +}; 43.125 +#endif 43.126 +
44.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 44.2 +++ b/src/gbasound.c Fri Mar 13 00:39:12 2009 -0700 44.3 @@ -0,0 +1,224 @@ 44.4 +#include <string.h> 44.5 +#include "ljgba.h" 44.6 +#include "pin8gba_sound.h" 44.7 +extern const unsigned short note_freqs[]; 44.8 + 44.9 +static const u16 moveEffect[] = { 44.10 + 0, 3, 44.11 + 0x516D, 0x0000, 0x0100 44.12 +}; 44.13 + 44.14 +static const u16 rotateEffect[] = { 44.15 + 1, 7, 44.16 + 0x5161, 0x0000, 0x5166, 0x0000, 0x516b, 0x0000, 0x0100 44.17 +}; 44.18 + 44.19 +static const u16 landEffect[] = { 44.20 + 1, 6, 44.21 + 0x8188, 0x7185, 0x6182, 0x5181, 0x3180, 0x0000 44.22 +}; 44.23 + 44.24 +static const u16 lockEffect[] = { 44.25 + 3, 1, 44.26 + 0x2138 44.27 +}; 44.28 + 44.29 +static const u16 lineEffect[] = { 44.30 + 0, 12, 44.31 + 0xB1A4, 0xA1A6, 0x91A9, 0x81A4, 0x71A7, 0x61A9, 44.32 + 0x61A4, 0x51A6, 0x41A9, 0x31A4, 0x21A7, 0x11A9 44.33 +}; 44.34 + 44.35 +static const u16 holdEffect[] = { 44.36 + 3, 8, 44.37 + 0x3030, 0x0000, 0x4032, 0x0000, 0x3130, 0x0000, 0x202c, 0x2128 44.38 +}; 44.39 + 44.40 +static const u16 irsRotateEffect[] = { 44.41 + 1, 12, 44.42 + 0x5161, 0x51B1, 0x5166, 0x51B6, 0x516B, 0x51BB, 0x0000, 0x0000, 44.43 + 0x0000, 0x0000, 0x0000, 0x0100 44.44 +}; 44.45 + 44.46 +static const u16 homerEffect[] = { 44.47 + 0, 22, 44.48 + 0xB1A4, 0xA1A6, 0x91A9, 0x81A4, 0x71A7, 0x61A9, 44.49 + 0x61A4, 0x51A6, 0x41A9, 0x31A4, 44.50 + 0xB1A7, 0xA1A9, 0x91AC, 0x81A7, 0x71AA, 0x61AC, 44.51 + 0x51A7, 0x41A9, 0x31AC, 0x21A7, 0x11AA, 0x01AC 44.52 +}; 44.53 + 44.54 +static const u16 streakEffect[] = { 44.55 + 0, 30, 44.56 + 0xB1A4, 0xA1A6, 0x91A9, 0x81A4, 0x71A7, 0x61A9, 44.57 + 0x61A4, 0x51A6, 0x41A9, 0x31A4, 44.58 + 0xB1A7, 0xA1A9, 0x91AC, 0x81A7, 0x71AA, 0x61AC, 44.59 + 0x51A7, 0x41A9, 0x31AC, 0x21A7, 44.60 + 0xB1AA, 0xA1AC, 0x91AF, 0x71AA, 0x61AD, 0x51AF, 44.61 + 0x31AA, 0x21AC, 0x11AF, 0x01AA 44.62 +}; 44.63 + 44.64 +static const u16 sectionEffect[] = { 44.65 + 1, 30, 44.66 + 0xB1B0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 44.67 + 0xB1B4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 44.68 + 0xB1B7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 44.69 + 0xB1BC, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 44.70 + 0xB1B0, 0x0000 44.71 +}; 44.72 + 44.73 +static const u16 gameOverEffect1[] = { 44.74 + 1, 40, 44.75 + 0xF504, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 44.76 + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 44.77 + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 44.78 + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 44.79 + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 44.80 +}; 44.81 + 44.82 +static const u16 gameOverEffect2[] = { 44.83 + 3, 40, 44.84 + 0xC12C, 0xA725, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 44.85 + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 44.86 + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 44.87 + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 44.88 + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 44.89 +}; 44.90 + 44.91 +static const u16 winEffect1[] = { 44.92 + 1, 30, 44.93 + 0xA564, 0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0000, 0x0000, 0x0000, 44.94 + 0xA562, 0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0000, 0x0000, 0x0000, 44.95 + 0xA564, 0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0000, 0x0000, 0x0000, 44.96 + 0xA566, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 44.97 + 0x0000, 0x0000 44.98 +}; 44.99 + 44.100 +static const u16 winEffect2[] = { 44.101 + 0, 30, 44.102 + 0xA568, 0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0000, 0x0000, 0x0000, 44.103 + 0xA566, 0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0000, 0x0000, 0x0000, 44.104 + 0xA568, 0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0000, 0x0000, 0x0000, 44.105 + 0xA56A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 44.106 + 0x0000, 0x0000 44.107 +}; 44.108 + 44.109 + 44.110 +static const u16 *const effects[] = { 44.111 + moveEffect, rotateEffect, landEffect, lockEffect, lineEffect, 44.112 + holdEffect, irsRotateEffect, homerEffect, streakEffect, sectionEffect, 44.113 + gameOverEffect1, gameOverEffect2, winEffect1, winEffect2 44.114 +}; 44.115 + 44.116 +const unsigned short tri_vol_table[5] = { 44.117 + 0, 44.118 + TRILENVOL_25, 44.119 + TRILENVOL_50, 44.120 + TRILENVOL_75, 44.121 + TRILENVOL_100 44.122 +}; 44.123 + 44.124 +void play_note(unsigned int ch, unsigned int note, unsigned int instrument) { 44.125 + switch (ch) { 44.126 + case 0: 44.127 + SQR1CTRL = instrument; 44.128 + SQR1FREQ = note_freqs[note] | FREQ_RESET; 44.129 + break; 44.130 + case 1: 44.131 + SQR2CTRL = instrument; 44.132 + SQR2FREQ = note_freqs[note] | FREQ_RESET; 44.133 + break; 44.134 + case 2: 44.135 + { 44.136 + int vol = instrument >> 13; 44.137 + TRILENVOL = tri_vol_table[(vol + 1) / 2]; 44.138 + TRIFREQ = note_freqs[note] | FREQ_RESET; 44.139 + } 44.140 + break; 44.141 + case 3: 44.142 + NOISECTRL = instrument; 44.143 + { 44.144 + int octave = (note & 0x3E) << 2; 44.145 + int pitch = note & 0x03; 44.146 + NOISEFREQ = (octave | pitch) ^ (0xF7 | FREQ_RESET); 44.147 + } 44.148 + break; 44.149 + } 44.150 +} 44.151 + 44.152 +void gba_poll_sound(struct LJPCView *v) { 44.153 + for (int i = 0; i < 4; ++i) { 44.154 + if (v->sndLeft[i]) { 44.155 + unsigned int data = *v->sndData[i]; 44.156 + if (data) { 44.157 + play_note(i, data & 0x003F, data & 0xFFC0); 44.158 + } 44.159 + ++v->sndData[i]; 44.160 + --v->sndLeft[i]; 44.161 + } 44.162 + } 44.163 +} 44.164 + 44.165 +void gba_play_sound(struct LJPCView *v, int effect) { 44.166 + const u16 *data = effects[effect]; 44.167 + int ch = data[0]; 44.168 + int len = data[1]; 44.169 + 44.170 + // square waves can be played on either channel 0 or 1; 44.171 + // switch if this channel is occupied and the other is free 44.172 + if (ch < 2 44.173 + && v->sndLeft[ch] > v->sndLeft[1 - ch]) { 44.174 + ch = 1 - ch; 44.175 + } 44.176 + v->sndData[ch] = data + 2; 44.177 + v->sndLeft[ch] = len; 44.178 +} 44.179 + 44.180 +/** 44.181 + * Sets the sound bias to mid and resolution to 8-bit. 44.182 + * Setting bias is needed because some launchers (such as pogoshell) 44.183 + * set it to a state that mutes the tone generators. 44.184 + */ 44.185 +static void set_bias(void) 44.186 +{ 44.187 + asm volatile("mov r2, #2; lsl r2, #8; swi 0x19" ::: "r0", "r1", "r2", "r3"); 44.188 + SETSNDRES(1); 44.189 +} 44.190 + 44.191 +__attribute__((aligned(4))) 44.192 +static const unsigned char triangleWave[16] = { 44.193 + 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 44.194 + 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 44.195 +}; 44.196 +unsigned char lastWaveBank = 0; 44.197 + 44.198 +void tri_set_waveform(const void *waveform) { 44.199 + memcpy((void *)TRIWAVERAM, waveform, 16); 44.200 + lastWaveBank = !lastWaveBank; 44.201 + TRICTRL = TRICTRL_2X32 | TRICTRL_BANK(lastWaveBank) | TRICTRL_ENABLE; 44.202 +} 44.203 + 44.204 +void install_sound(struct LJPCView *v) { 44.205 + SNDSTAT = SNDSTAT_ENABLE; 44.206 + DMGSNDCTRL = DMGSNDCTRL_LVOL(7) | DMGSNDCTRL_RVOL(7) 44.207 + | DMGSNDCTRL_LSQR1 | DMGSNDCTRL_RSQR1 44.208 + | DMGSNDCTRL_LSQR2 | DMGSNDCTRL_RSQR2 44.209 + | DMGSNDCTRL_LTRI | DMGSNDCTRL_RTRI 44.210 + | DMGSNDCTRL_LNOISE | DMGSNDCTRL_RNOISE; 44.211 + DSOUNDCTRL = DSOUNDCTRL_DMG100; 44.212 + set_bias(); 44.213 + SQR1SWEEP = SQR1SWEEP_OFF; 44.214 + 44.215 +#if 0 44.216 + TRICTRL = TRICTRL_2X32 | TRICTRL_BANK(0) | TRICTRL_ENABLE; 44.217 + tri_set_waveform(triangleWave); 44.218 + TRILENVOL = 0; 44.219 + TRIFREQ = 1024 | FREQ_RESET; 44.220 +#endif 44.221 + 44.222 + v->sndLeft[0] = 0; 44.223 + v->sndLeft[1] = 0; 44.224 + v->sndLeft[2] = 0; 44.225 + v->sndLeft[3] = 0; 44.226 +} 44.227 +
45.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 45.2 +++ b/src/gimmicks.c Fri Mar 13 00:39:12 2009 -0700 45.3 @@ -0,0 +1,295 @@ 45.4 +/* Gimmick code for LOCKJAW, an implementation of the Soviet Mind Game 45.5 + 45.6 +Copyright (C) 2006-2007 Damian Yerrick <tepples+lj@spamcop.net> 45.7 + 45.8 +This work is free software; you can redistribute it and/or modify 45.9 +it under the terms of the GNU General Public License as published by 45.10 +the Free Software Foundation; either version 2 of the License, or 45.11 +(at your option) any later version. 45.12 + 45.13 +This program is distributed in the hope that it will be useful, 45.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 45.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 45.16 +GNU General Public License for more details. 45.17 + 45.18 +You should have received a copy of the GNU General Public License 45.19 +along with this program; if not, write to the Free Software 45.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 45.21 + 45.22 +Original game concept and design by Alexey Pajitnov. 45.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 45.24 +or The Tetris Company LLC. 45.25 + 45.26 +*/ 45.27 + 45.28 +#include "lj.h" 45.29 +#include "ljcontrol.h" 45.30 + 45.31 +void initSpeed(LJField *p); // in speed.c 45.32 +void setSpeed(LJField *p, LJControl *c); // in speed.c 45.33 +int updLevelAfterPiece(LJField *p); // in speed.c 45.34 +int updLevelAfterLines(LJField *p, size_t nLines); // in speed.c 45.35 + 45.36 +void initGimmicks(LJField *p) { 45.37 + initSpeed(p); 45.38 + p->speed.entryDelay = 0; /* new pieces will increase this */ 45.39 + p->speed.lineDelay = 0; 45.40 + if (p->garbageStyle == LJGARBAGE_DRILL 45.41 + || p->gimmick == LJGM_DRILL) { 45.42 + p->garbageRandomness = 255; 45.43 + p->garbage = p->ceiling - 2; 45.44 + } else if (p->garbageStyle == LJGARBAGE_ZIGZAG) { 45.45 + setupZigzagField(p, p->ceiling * 3 / 4); 45.46 + } 45.47 + 45.48 +} 45.49 + 45.50 +LJBits gimmicks(LJField *p, LJControl *c) { 45.51 + LJBits changed = 0; 45.52 + 45.53 + // In rhythm, lock the tetromino if the player 45.54 + // isn't keeping up with the rate 45.55 + if (p->speedState.curve == LJSPD_RHYTHM 45.56 + || p->speedState.curve == LJSPD_RHYTHMZERO) { 45.57 + p->bpmCounter += p->speedState.level; 45.58 + 45.59 + if (p->bpmCounter >= 0) { 45.60 + if (p->state == LJS_LANDED) { 45.61 + p->stateTime = 0; 45.62 + } else if (p->state == LJS_FALLING) { 45.63 + p->speed.gravity = ljitofix(LJ_PF_HT); 45.64 + } 45.65 + } 45.66 + 45.67 + // In rhythm, increase BPM periodically 45.68 + p->speedupCounter += p->speedState.level; 45.69 + if(p->speedupCounter >= 60 * 60 * 64) { 45.70 + p->speedupCounter -= 60 * 60 * 64; 45.71 + p->speedState.level += 10; 45.72 + p->sounds |= LJSND_SECTIONUP; 45.73 + } 45.74 + } 45.75 + 45.76 + // For each piece, set the entry and line delays. 45.77 + // Don't set it twice when spawning to replace the 45.78 + // first held piece (both LJSND_SPAWN and LJSND_HOLD 45.79 + // on the same piece). 45.80 + if ((p->sounds & (LJSND_SPAWN | LJSND_HOLD)) 45.81 + == LJSND_SPAWN) { 45.82 + setSpeed(p, c); 45.83 + } 45.84 + 45.85 + if (p->sounds & LJSND_LOCK) { 45.86 + if (p->gimmick == LJGM_ITEMS) { 45.87 + if (p->nPieces >= 7) { 45.88 + p->canRotate = 0; 45.89 + p->speed.gravity = ljitofix(1); 45.90 + } 45.91 + } 45.92 + 45.93 + // Garbage in simulated multiplayer 45.94 + int simGarbage = p->nPieces >= 7 45.95 + && p->garbageStyle >= LJGARBAGE_1 45.96 + && p->garbageStyle <= LJGARBAGE_4 45.97 + && (p->curPiece[1] == LJP_I 45.98 + || ((p->pieceSet == LJRAND_SZ 45.99 + || p->pieceSet == LJRAND_JLOSTZ) 45.100 + && p->nPieces % 7 == 3)); 45.101 + if (simGarbage) { 45.102 + p->garbage += p->garbageStyle; 45.103 + } 45.104 + 45.105 + // Banana attack in "Vs. with Items" gimmick 45.106 + if (p->gimmick == LJGM_ITEMS 45.107 + && (ljRand(p) & 0xF00) == 0xF00) { 45.108 + shuffleColumns(p); 45.109 + changed |= (1 << LJ_PF_HT) - 1; 45.110 + } 45.111 + } 45.112 + return changed; 45.113 +} 45.114 + 45.115 + 45.116 + 45.117 +const char hotlineRows[LJ_PF_HT] = 45.118 +{ 45.119 + 0, 0, 0, 0, 1, 45.120 + 0, 0, 0, 0, 2, 45.121 + 0, 0, 0, 3, 0, 45.122 + 0, 4, 0, 5, 6, 45.123 + 0, 0, 0, 0 45.124 +}; 45.125 + 45.126 +static const char garbageScoreTable[] = { 0, 0, 1, 2, 4 }; 45.127 +static const char tSpinGarbageScoreTable[] = { 0, 2, 4, 6, 6 }; 45.128 +static const unsigned char squareScoreTable[] = 45.129 + {1, 1, 1, 2, 3, 5, 8, 13, 45.130 + 21, 34, 55, 89, 144, 200}; 45.131 + 45.132 +static const char tdsScoreTable[] = {0, 1, 3, 5, 8}; 45.133 +static const char tdsTSScoreTable[] = {4, 8, 12, 16}; 45.134 +static const char nesScoreTable[] = {0, 4, 10, 30, 120}; 45.135 + 45.136 + 45.137 +/** 45.138 + * Computes the score and outgoing garbage for lines 45.139 + * and adds them to the player's total. 45.140 + * @param p The playfield 45.141 + * @param lines Bit array where 1 means a line clear on this row. 45.142 + */ 45.143 +void addLinesScore(LJField *p, LJBits lines) { 45.144 + const int nLines = countOnes(lines); 45.145 + int oldLines = p->nLinesThisPiece; 45.146 + int tdsSection = p->lines / 10 + 1; 45.147 + 45.148 + p->lines += nLines; 45.149 + if (updLevelAfterLines(p, nLines)) { 45.150 + p->sounds |= LJSND_SECTIONUP; 45.151 + } 45.152 + 45.153 + switch (p->scoreStyle) { 45.154 + 45.155 + case LJSCORE_TNT64: 45.156 + if (nLines < 1) { 45.157 + return; 45.158 + } 45.159 + for (int i = nLines; i > 0; --i) { 45.160 + if (oldLines > sizeof(squareScoreTable) - 1) { 45.161 + oldLines = sizeof(squareScoreTable) - 1; 45.162 + } 45.163 + p->score += 100 * squareScoreTable[oldLines++]; 45.164 + } break; 45.165 + 45.166 + case LJSCORE_NES: 45.167 + { 45.168 + int garbageLevel = (nLines > 4) ? 4 : nLines; 45.169 + int value = nesScoreTable[garbageLevel]; 45.170 + 45.171 + p->score += 10 * tdsSection * value; 45.172 + if (nLines >= 4) { 45.173 + p->sounds |= LJSND_SETB2B; 45.174 + } 45.175 + } break; 45.176 + 45.177 + case LJSCORE_TDS: 45.178 + { 45.179 + // Garbage based scoring system. 45.180 + int garbageLevel = (nLines > 4) ? 4 : nLines; 45.181 + int chain, value; 45.182 + 45.183 + if (p->isSpin) { 45.184 + chain = (nLines >= 1); 45.185 + value = tdsTSScoreTable[garbageLevel]; 45.186 + 45.187 + // TDS gives fewer points for a T-spin single 45.188 + // that involves a wall kick. 45.189 + if (p->isSpin == 2 && nLines < 2) { 45.190 + value >>= 2; 45.191 + } 45.192 + } else { 45.193 + chain = (nLines >= 4); 45.194 + value = tdsScoreTable[garbageLevel]; 45.195 + } 45.196 + if (chain && p->chain && nLines >= 1) { // b2b 45.197 + value = value * 3 / 2; 45.198 + } 45.199 + 45.200 + if (tdsSection > 20) { 45.201 + tdsSection = 20; 45.202 + } 45.203 + p->score += 100 * tdsSection * value; 45.204 + if (nLines >= 1) { 45.205 + if (chain) { 45.206 + p->sounds |= LJSND_SETB2B; 45.207 + if (p->chain) { 45.208 + p->sounds |= LJSND_B2B; 45.209 + } 45.210 + } 45.211 + p->chain = chain; 45.212 + } 45.213 + } break; 45.214 + 45.215 + case LJSCORE_LJ: 45.216 + case LJSCORE_LJ_NERF: 45.217 + if (nLines >= 1) { 45.218 + // Garbage based scoring system. 45.219 + int garbageLevel = (nLines > 4) ? 4 : nLines; 45.220 + unsigned int chain, garbage; 45.221 + 45.222 + if (p->isSpin) { 45.223 + chain = (nLines >= 1); 45.224 + garbage = tSpinGarbageScoreTable[garbageLevel]; 45.225 + if (p->scoreStyle == LJSCORE_LJ_NERF) { 45.226 + garbage /= 2; 45.227 + } 45.228 + garbage += (chain && p->chain); 45.229 + } else { 45.230 + chain = (nLines >= 4); 45.231 + garbage = garbageScoreTable[garbageLevel] 45.232 + + (chain && p->chain); 45.233 + } 45.234 + p->score += 100 * nLines + 200 * garbage; 45.235 + p->outGarbage += garbage; 45.236 + if (chain) { 45.237 + p->sounds |= LJSND_SETB2B; 45.238 + if (p->chain) { 45.239 + p->sounds |= LJSND_B2B; 45.240 + } 45.241 + } 45.242 + p->chain = chain; 45.243 + } break; 45.244 + 45.245 + case LJSCORE_HOTLINE: 45.246 + for (int y = 0; y < 24; ++y) { 45.247 + if (lines & (1 << y)) { 45.248 + p->score += 100 * hotlineRows[y]; 45.249 + } 45.250 + } break; 45.251 + 45.252 + } 45.253 + 45.254 + p->nLinesThisPiece += nLines; 45.255 +} 45.256 + 45.257 +/** 45.258 + * Puts a zigzag pattern of garbage into the playfield. 45.259 + * Useful as a test suite for calcZigzagGrade() in debrief.c. 45.260 + * @param p the playfield to set up 45.261 + * @param height the number of rows to set up 45.262 + */ 45.263 +void setupZigzagField(LJField *p, size_t height) { 45.264 + unsigned int hole = p->leftWall; 45.265 + int delta = 1; 45.266 + 45.267 + if (height > p->ceiling) { 45.268 + height = p->ceiling; 45.269 + } 45.270 + 45.271 + // set up example pattern 45.272 + for (size_t y = 0; y < height; ++y) { 45.273 + for (size_t x = p->leftWall; x < p->rightWall; ++x) { 45.274 + 45.275 + // A block should be in all cells of the row 45.276 + // except for the hole cell, which should be empty. 45.277 + if (x == hole) { 45.278 + p->b[y][x] = 0; 45.279 + } else { 45.280 + p->b[y][x] = 0x80; 45.281 + } 45.282 + } 45.283 + 45.284 + if (hole == p->rightWall - 1) { 45.285 + delta = -1; 45.286 + } else if (hole == p->leftWall) { 45.287 + delta = 1; 45.288 + } 45.289 + hole += delta; 45.290 + } 45.291 + 45.292 + // clear rows above pattern 45.293 + for (size_t y = height; y < LJ_PF_HT; ++y) { 45.294 + for (size_t x = p->leftWall; x < p->rightWall; ++x) { 45.295 + p->b[y][x] = 0; 45.296 + } 45.297 + } 45.298 +}
46.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 46.2 +++ b/src/lj.c Fri Mar 13 00:39:12 2009 -0700 46.3 @@ -0,0 +1,1516 @@ 46.4 +/* Engine of LOCKJAW, an implementation of the Soviet Mind Game 46.5 + 46.6 +Copyright (C) 2006 Damian Yerrick <tepples+lj@spamcop.net> 46.7 + 46.8 +This work is free software; you can redistribute it and/or modify 46.9 +it under the terms of the GNU General Public License as published by 46.10 +the Free Software Foundation; either version 2 of the License, or 46.11 +(at your option) any later version. 46.12 + 46.13 +This program is distributed in the hope that it will be useful, 46.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 46.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 46.16 +GNU General Public License for more details. 46.17 + 46.18 +You should have received a copy of the GNU General Public License 46.19 +along with this program; if not, write to the Free Software 46.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 46.21 + 46.22 +Original game concept and design by Alexey Pajitnov. 46.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 46.24 +or The Tetris Company LLC. 46.25 + 46.26 + 46.27 +*/ 46.28 + 46.29 +#define LJ_INTERNAL 46.30 +#include "lj.h" 46.31 + 46.32 +unsigned int ljRand(LJField *p) { 46.33 + p->seed = p->seed * 2147001325 + 715136305; 46.34 + return p->seed >> 17; 46.35 +} 46.36 + 46.37 +static inline void ljAssert(LJField *p, int shouldBeTrue, const char *reason) { 46.38 + if (!shouldBeTrue) { 46.39 + p->state = LJS_GAMEOVER; 46.40 + } 46.41 +} 46.42 + 46.43 +static const char xShapes[N_PIECE_SHAPES][4][4] = { 46.44 + { {0,1,2,3}, {2,2,2,2}, {3,2,1,0}, {1,1,1,1} }, // I 46.45 + { {0,1,2,0}, {1,1,1,2}, {2,1,0,2}, {1,1,1,0} }, // J 46.46 + { {0,1,2,2}, {1,1,1,2}, {2,1,0,0}, {1,1,1,0} }, // L 46.47 + { {1,1,2,2}, {1,2,2,1}, {2,2,1,1}, {2,1,1,2} }, // O 46.48 + { {0,1,1,2}, {1,1,2,2}, {2,1,1,0}, {1,1,0,0} }, // S 46.49 + { {0,1,2,1}, {1,1,1,2}, {2,1,0,1}, {1,1,1,0} }, // T 46.50 + { {0,1,1,2}, {2,2,1,1}, {2,1,1,0}, {0,0,1,1} }, // Z 46.51 + { {0,1,2,3}, {2,2,2,2}, {3,2,1,0}, {1,1,1,1} }, // I2 46.52 + { {0,1,2,0}, {1,1,1,2}, {2,1,0,2}, {1,1,1,0} }, // I3 46.53 + { {1,1,2,2}, {1,2,2,1}, {2,2,1,1}, {2,1,1,2} }, // L3 46.54 +}; 46.55 + 46.56 +static const char yShapes[N_PIECE_SHAPES][4][4] = { 46.57 + { {2,2,2,2}, {3,2,1,0}, {1,1,1,1}, {0,1,2,3} }, // I 46.58 + { {2,2,2,3}, {3,2,1,3}, {2,2,2,1}, {1,2,3,1} }, // J 46.59 + { {2,2,2,3}, {3,2,1,1}, {2,2,2,1}, {1,2,3,3} }, // L 46.60 + { {2,3,3,2}, {3,3,2,2}, {3,2,2,3}, {2,2,3,3} }, // O 46.61 + { {2,2,3,3}, {3,2,2,1}, {2,2,1,1}, {1,2,2,3} }, // S 46.62 + { {2,2,2,3}, {3,2,1,2}, {2,2,2,1}, {1,2,3,2} }, // T 46.63 + { {3,3,2,2}, {3,2,2,1}, {1,1,2,2}, {1,2,2,3} }, // Z 46.64 + { {2,2,2,2}, {3,2,1,0}, {1,1,1,1}, {0,1,2,3} }, // I2 46.65 + { {2,2,2,3}, {3,2,1,3}, {2,2,2,1}, {1,2,3,1} }, // I3 46.66 + { {2,3,3,2}, {3,3,2,2}, {3,2,2,3}, {2,2,3,3} }, // L3 46.67 +}; 46.68 + 46.69 +const char pieceColors[N_PIECE_SHAPES] = { 46.70 + 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 46.71 + 0x40, 0x10, 0x20 46.72 +}; 46.73 + 46.74 +static const signed char connShapes[N_PIECE_SHAPES][4] = { 46.75 + {CONNECT_R, CONNECT_LR, CONNECT_LR, CONNECT_L}, // I 46.76 + {CONNECT_UR, CONNECT_LR, CONNECT_L, CONNECT_D}, // J 46.77 + {CONNECT_R, CONNECT_LR, CONNECT_UL, CONNECT_D}, // L 46.78 + {CONNECT_UR, CONNECT_DR, CONNECT_DL, CONNECT_UL}, // O 46.79 + {CONNECT_R, CONNECT_UL, CONNECT_DR, CONNECT_L}, // S 46.80 + {CONNECT_R, CONNECT_ULR, CONNECT_L, CONNECT_D}, // T 46.81 + {CONNECT_R, CONNECT_DL, CONNECT_UR, CONNECT_L}, // Z 46.82 + {-1, CONNECT_R, CONNECT_L, -1}, // I2 46.83 + {CONNECT_R, CONNECT_LR, CONNECT_L, -1}, // I3 46.84 + {CONNECT_UR, CONNECT_D, -1, CONNECT_L}, // L3 46.85 +}; 46.86 + 46.87 +static inline int pieceToFieldBlock(int piece, int conn) { 46.88 + return conn | pieceColors[piece]; 46.89 +} 46.90 + 46.91 +/** 46.92 + * @theta rotation state: 0-3 normal, 4 for next piece 46.93 + */ 46.94 +void expandPieceToBlocks(LJBlkSpec out[], 46.95 + const LJField *p, int piece, int xBase, int yBase, int theta) { 46.96 + int shape = piece & LJP_MASK; 46.97 + 46.98 + if (theta >= 4) { 46.99 + const LJRotSystem *rs = rotSystems[p->rotationSystem]; 46.100 + int kickData = rs->entryOffset[shape]; 46.101 + xBase += WKX(kickData); 46.102 + yBase += WKY(kickData); 46.103 + kickData = rs->entryOffset[LJP_I]; 46.104 + xBase -= WKX(kickData); 46.105 + yBase -= WKY(kickData); 46.106 + theta = rs->entryTheta[shape]; 46.107 + } 46.108 + 46.109 + const char *xBl = xShapes[shape][theta]; 46.110 + const char *yBl = yShapes[shape][theta]; 46.111 + 46.112 + for (int blk = 0; blk < 4; ++blk) { 46.113 + if (connShapes[shape][blk] == -1) { 46.114 + out[blk].conn = 0; 46.115 + } else { 46.116 + int conn = connShapes[shape][blk] << theta; 46.117 + int connRotated = (conn | (conn >> 4)) & CONNECT_MASK; 46.118 + int color = ((0x10 << blk) & piece) ? 8 : piece; 46.119 + out[blk].y = yBl[blk] + yBase; 46.120 + out[blk].x = xBl[blk] + xBase; 46.121 + out[blk].conn = pieceToFieldBlock(color, connRotated); 46.122 + } 46.123 + } 46.124 +} 46.125 + 46.126 +int isOccupied(const LJField *p, int x, int y) { 46.127 + if (x < p->leftWall || x >= p->rightWall || y < 0) 46.128 + return 1; 46.129 + if (y > LJ_PF_HT) 46.130 + return 0; 46.131 + return p->b[y][x] > 1; 46.132 +} 46.133 + 46.134 +int isCollision(const LJField *p, int x, int y, int theta) { 46.135 + LJBlkSpec blocks[4]; 46.136 + int piece = p->curPiece[0]; 46.137 + 46.138 + expandPieceToBlocks(blocks, p, piece, x, y, theta); 46.139 + 46.140 + for (int blk = 0; blk < 4; ++blk) { 46.141 + if (blocks[blk].conn 46.142 + && isOccupied(p, blocks[blk].x, blocks[blk].y)) 46.143 + return 1; 46.144 + } 46.145 + return 0; 46.146 +} 46.147 + 46.148 +/** 46.149 + * Determines whether the piece that just landed was a T-spin. 46.150 + * Must be called just BEFORE lockdown writes the blocks to the 46.151 + * playfield; otherwise TNT will break. 46.152 + */ 46.153 +static int isTspin(const LJField *p) { 46.154 + int blks = 0; 46.155 + int x = p->x; 46.156 + int y = p->hardDropY; 46.157 + 46.158 + switch (p->tSpinAlgo) { 46.159 + case LJTS_TNT: 46.160 + if (!isCollision(p, x, y + 1, p->theta) 46.161 + || !isCollision(p, x - 1, y, p->theta) 46.162 + || !isCollision(p, x + 1, y, p->theta)) { 46.163 + return 0; 46.164 + } 46.165 + return p->isSpin; 46.166 + case LJTS_TDS_NO_KICK: 46.167 + 46.168 + // If t-spin involved wall kick, don't count it 46.169 + if (p->isSpin == 2) { 46.170 + return 0; 46.171 + } 46.172 + 46.173 + // otherwise fall through 46.174 + case LJTS_TDS: 46.175 + // 1. T tetromino 46.176 + if ((p->curPiece[0] & LJP_MASK) != LJP_T) { 46.177 + return 0; 46.178 + } 46.179 + 46.180 + // 2. Last move was spin 46.181 + if (!p->isSpin) { 46.182 + return 0; 46.183 + } 46.184 + 46.185 + // 3. At least three cells around the rotation center are full 46.186 + if (isOccupied(p, x, y + 1)) { 46.187 + ++blks; 46.188 + } 46.189 + if (isOccupied(p, x, y + 3)) { 46.190 + ++blks; 46.191 + } 46.192 + if (isOccupied(p, x + 2, y + 1)) { 46.193 + ++blks; 46.194 + } 46.195 + if (isOccupied(p, x + 2, y + 3)) { 46.196 + ++blks; 46.197 + } 46.198 + if (blks < 3) { 46.199 + return 0; 46.200 + } 46.201 + 46.202 + // 3. Last move was spin 46.203 + return p->isSpin; 46.204 + default: 46.205 + return 0; 46.206 + } 46.207 +} 46.208 + 46.209 +/** 46.210 + * Calculates where the active piece in a playfield will fall 46.211 + * if dropped, and writes it back to the playfield. 46.212 + * This value is used for ghost piece, gravity, soft drop, and 46.213 + * hard drop. Call this after the active piece has been spawned, 46.214 + * moved, or rotated. 46.215 + * @param p the playfield 46.216 + */ 46.217 +static void updHardDropY(LJField *p) { 46.218 + int x = p->x; 46.219 + int y = ljfixfloor(p->y); 46.220 + int theta = p->theta; 46.221 + 46.222 + if (p->bottomBlocks) { 46.223 + y = -2; 46.224 + while (y < (int)LJ_PF_HT 46.225 + && y < ljfixfloor(p->y) 46.226 + && isCollision(p, x, y, theta)) { 46.227 + ++y; 46.228 + } 46.229 + } 46.230 + else { 46.231 + while (!isCollision(p, x, y - 1, theta)) { 46.232 + --y; 46.233 + } 46.234 + } 46.235 + p->hardDropY = y; 46.236 +} 46.237 + 46.238 + 46.239 +/** 46.240 + * Look for a TNT square in this position. 46.241 + * @param x column of left side, such that 0 <= x <= playfield width - 4 46.242 + * @param y row of bottom block, such that 0 <= y <= playfield height - 4 46.243 + * @param isMulti nonzero for multisquares; 0 for monosquares 46.244 + * @return nonzero if found; 0 if not found 46.245 + */ 46.246 +static int isSquareAt(LJField *p, int x, int y, int isMulti) 46.247 +{ 46.248 + int firstColor = p->b[y][x] & COLOR_MASK; 46.249 + 46.250 + // Check the frame to make sure it isn't connected to anything else 46.251 + for(int i = 0; i <= 3; i++) 46.252 + { 46.253 + /* don't allow squares within parts of squares */ 46.254 + if((p->b[y + i][x] & COLOR_MASK) >= 0x80) 46.255 + return 0; 46.256 + /* the block doesn't connect on the left */ 46.257 + if(p->b[y + i][x] & CONNECT_L) 46.258 + return 0; 46.259 + /* the block doesn't connect on the right */ 46.260 + if(p->b[y + i][x + 3] & CONNECT_R) 46.261 + return 0; 46.262 + /* the block doesn't connect on the bottom */ 46.263 + if(p->b[y][x + i] & CONNECT_D) 46.264 + return 0; 46.265 + /* the block doesn't connect on the top */ 46.266 + if(p->b[y + 3][x + i] & CONNECT_U) 46.267 + return 0; 46.268 + } 46.269 + 46.270 + for(int ySub = 0; ySub < 4; ++ySub) 46.271 + { 46.272 + for(int xSub = 0; xSub <= 3; ++xSub) 46.273 + { 46.274 + int blkHere = p->b[y + ySub][x + xSub]; 46.275 + 46.276 + /* the square contains no nonexistent blocks */ 46.277 + if(!blkHere) 46.278 + return 0; 46.279 + /* the square contains no blocks of garbage or broken pieces */ 46.280 + if((blkHere & COLOR_MASK) == 0x80) 46.281 + return 0; 46.282 + /* if looking for monosquares, disallow multisquares */ 46.283 + if(isMulti == 0 && (blkHere & COLOR_MASK) != firstColor) 46.284 + return 0; 46.285 + } 46.286 + } 46.287 + return 1; 46.288 +} 46.289 + 46.290 +/** 46.291 + * Replaces the 4x4 blocks with a 4x4 square. 46.292 + * @param x column of left side, such that 0 <= x <= playfield width - 4 46.293 + * @param y row of bottom block, such that 0 <= y <= playfield height - 4 46.294 + * @param isMulti nonzero for multisquares; 0 for monosquares 46.295 + * @return the rows that were changed 46.296 + */ 46.297 +static LJBits markSquare(LJField *p, int x, int y, int isMulti) 46.298 +{ 46.299 + int baseBlk = (isMulti ? 0xA0 : 0xB0) 46.300 + | CONNECT_MASK; 46.301 + for(int i = 0; i < 4; ++i) 46.302 + { 46.303 + int c; 46.304 + 46.305 + if(i == 0) 46.306 + c = baseBlk & ~CONNECT_D; 46.307 + else if(i == 3) 46.308 + c = baseBlk & ~CONNECT_U; 46.309 + else 46.310 + c = baseBlk; 46.311 + 46.312 + p->b[y + i][x + 0] = c & ~CONNECT_L; 46.313 + p->b[y + i][x + 1] = c; 46.314 + p->b[y + i][x + 2] = c; 46.315 + p->b[y + i][x + 3] = c & ~CONNECT_R; 46.316 + } 46.317 + 46.318 + if (isMulti) { 46.319 + ++p->multisquares; 46.320 + } else { 46.321 + ++p->monosquares; 46.322 + } 46.323 + 46.324 + return 0x0F << y; 46.325 +} 46.326 + 46.327 + 46.328 +/** 46.329 + * Marks all 4x4 squares in the playfield. 46.330 + * In the case that a single tetromino forms multiple overlapping 46.331 + * squares, prefers gold over silver, high over low, left over right. 46.332 + * @param isMulti nonzero for multisquares; 0 for monosquares 46.333 + * @return the rows that were changed 46.334 + */ 46.335 +static LJBits findSquares(LJField *p, int isMulti) { 46.336 + LJBits changed = 0; 46.337 + 46.338 + for (int y = LJ_PF_HT - 4; y >= 0; --y) { 46.339 + for (int x = p->leftWall; x <= p->rightWall - 4; ++x) { 46.340 + int baseBlk = p->b[y][x]; 46.341 + 46.342 + // If this block is filled in and not connected on the left or right 46.343 + // then do stuff to it 46.344 + if (baseBlk 46.345 + && !(baseBlk & (CONNECT_D | CONNECT_L)) 46.346 + && isSquareAt(p, x, y, isMulti)) { 46.347 + changed |= markSquare(p, x, y, isMulti); 46.348 + p->sounds |= LJSND_SQUARE; 46.349 + } 46.350 + } 46.351 + } 46.352 + return changed; 46.353 +} 46.354 + 46.355 +static LJBits stickyGluing(LJField *p) { 46.356 + LJBits changed = 0; 46.357 + int byColor = (p->gluing == LJGLUING_STICKY_BY_COLOR); 46.358 + 46.359 + for (int y = 0; y < LJ_PF_HT; ++y) { 46.360 + for (int x = p->leftWall; x < p->rightWall - 1; ++x) { 46.361 + int l = p->b[y][x]; 46.362 + int r = p->b[y][x + 1]; 46.363 + if (l && r 46.364 + && (!(l & CONNECT_R) || !(r & CONNECT_L)) 46.365 + && (!byColor || !((l ^ r) & COLOR_MASK))) { 46.366 + p->b[y][x] = l | CONNECT_R; 46.367 + p->b[y][x + 1] = r | CONNECT_L; 46.368 + changed |= 1 << y; 46.369 + } 46.370 + } 46.371 + } 46.372 + 46.373 + for (int y = 0; y < LJ_PF_HT - 1; ++y) { 46.374 + for (int x = p->leftWall; x < p->rightWall; ++x) { 46.375 + int b = p->b[y][x]; 46.376 + int t = p->b[y + 1][x]; 46.377 + if (b && t 46.378 + && (!(b & CONNECT_U) || !(t & CONNECT_D)) 46.379 + && (!byColor || !((b ^ t) & COLOR_MASK))) { 46.380 + p->b[y][x] = b | CONNECT_U; 46.381 + p->b[y + 1][x] = t | CONNECT_D; 46.382 + changed |= 3 << y; 46.383 + } 46.384 + } 46.385 + } 46.386 + 46.387 + return changed; 46.388 +} 46.389 + 46.390 +/** 46.391 + * Locks the current tetromino in the playfield. 46.392 + * @param p the playfield 46.393 + * @return the rows in which the tetromino was placed 46.394 + */ 46.395 +static LJBits lockPiece(LJField *p) { 46.396 + LJBits rows = 0; 46.397 + int xBase = p->x; 46.398 + int yBase = ljfixfloor(p->y); 46.399 + LJBlkSpec blocks[4]; 46.400 + int piece = p->curPiece[0]; 46.401 + expandPieceToBlocks(blocks, p, piece, xBase, yBase, p->theta); 46.402 + 46.403 + p->isSpin = isTspin(p); 46.404 + 46.405 + for (int blk = 0; blk < 4; ++blk) { 46.406 + int blkY = blocks[blk].y; 46.407 + int blkX = blocks[blk].x; 46.408 + int blkValue = blocks[blk].conn; 46.409 + 46.410 + if(blkValue && blkY >= 0 && blkY < LJ_PF_HT) { 46.411 + rows |= 1 << blkY; 46.412 + if (blkX >= p->leftWall && blkX < p->rightWall) { 46.413 + p->b[blkY][blkX] = blkValue; 46.414 + } 46.415 + } 46.416 + } 46.417 + p->sounds |= LJSND_LOCK; 46.418 + p->alreadyHeld = 0; 46.419 + p->nLinesThisPiece = 0; 46.420 + 46.421 + return rows; 46.422 +} 46.423 + 46.424 +void shuffleColumns(LJField *p) { 46.425 + unsigned int permu[LJ_PF_WID]; 46.426 + 46.427 + for (int x = p->leftWall; x < p->rightWall; ++x) { 46.428 + permu[x] = x; 46.429 + } 46.430 + for (int x = p->rightWall - 1; x > p->rightWall + 1; --x) { 46.431 + int r = ljRand(p) % x; 46.432 + int t = permu[x]; 46.433 + permu[x] = permu[r]; 46.434 + permu[r] = t; 46.435 + } 46.436 + 46.437 + for (int y = 0; y < LJ_PF_HT; ++y) { 46.438 + unsigned int blk[LJ_PF_WID]; 46.439 + 46.440 + // Copy blocks to temporary buffer, eliminating left and right connections 46.441 + for (int x = p->leftWall; x < p->rightWall; ++x) { 46.442 + blk[x] = p->b[y][permu[x]] & ~(CONNECT_L | CONNECT_R); 46.443 + } 46.444 + for (int x = p->leftWall; x < p->rightWall; ++x) { 46.445 + p->b[y][x] = blk[x]; 46.446 + } 46.447 + } 46.448 +} 46.449 + 46.450 +/** 46.451 + * Rotates and shifts a new piece per the rotation system. 46.452 + */ 46.453 +static void rotateNewPiece(LJField *p) { 46.454 + const LJRotSystem *rs = rotSystems[p->rotationSystem]; 46.455 + int shape = p->curPiece[0] & LJP_MASK; 46.456 + int kickData = rs->entryOffset[shape]; 46.457 + p->x += WKX(kickData); 46.458 + p->y += ljitofix(WKY(kickData)); 46.459 + p->theta = rs->entryTheta[shape]; 46.460 + 46.461 + /* Find extents of piece so that it doesn't enter already collided 46.462 + with the walls (otherwise, in Tengen rotation and well width 4, 46.463 + spawning I causes block out) */ 46.464 + 46.465 + LJBlkSpec blocks[4]; 46.466 + int minX = LJ_PF_WID - 1, maxX = 0, maxY = 0; 46.467 + 46.468 + expandPieceToBlocks(blocks, p, p->curPiece[0], 46.469 + p->x, ljfixfloor(p->y), p->theta); 46.470 + for (int blk = 0; blk < 4; ++blk) { 46.471 + if (blocks[blk].conn) { 46.472 + if (maxY < blocks[blk].y) { 46.473 + maxY = blocks[blk].y; 46.474 + } 46.475 + if (maxX < blocks[blk].x) { 46.476 + maxX = blocks[blk].x; 46.477 + } 46.478 + if (minX > blocks[blk].x) { 46.479 + minX = blocks[blk].x; 46.480 + } 46.481 + } 46.482 + } 46.483 + 46.484 + if (minX < p->leftWall) { 46.485 + p->x += minX - p->leftWall; 46.486 + } else if (maxX >= p->rightWall) { 46.487 + p->x -= (maxX + 1) - p->rightWall; 46.488 + } 46.489 + if (!p->enterAbove && maxY >= p->ceiling) { 46.490 + p->y -= ljitofix(maxY - p->ceiling) + 1; 46.491 + } 46.492 +} 46.493 + 46.494 + 46.495 +/** 46.496 + * Spawns a tetromino onto the playfield. 46.497 + * @param p the playfield 46.498 + * @param hold 0 for spawning from next; nonzero for swapping with hold 46.499 + * @return the rows that were changed by banana effect 46.500 + */ 46.501 +static LJBits newPiece(LJField *p, int hold) { 46.502 + LJBits changed = 0; 46.503 + int initial = p->state != LJS_FALLING 46.504 + && p->state != LJS_LANDED; 46.505 + int ihs = initial && hold; 46.506 + 46.507 + if (hold) { 46.508 + if (p->state == LJS_LANDED && p->lockReset == LJLOCK_SPAWN) { 46.509 + p->speed.lockDelay = p->stateTime; 46.510 + } 46.511 + } else { 46.512 + p->upwardKicks = 0; 46.513 + } 46.514 + p->x = (LJ_PF_WID - 4) / 2; 46.515 + p->dropDist = 0; 46.516 + if (!ihs) { 46.517 + p->state = LJS_FALLING; 46.518 + p->stateTime = 0; 46.519 + } 46.520 + p->isSpin = 0; 46.521 + p->y = ljitofix(p->ceiling - 2); 46.522 + 46.523 + /* Note: The gimmick sets the gravity speed after frame() finishes. */ 46.524 + 46.525 + if (hold) { 46.526 + int temp; 46.527 + 46.528 + if (p->holdStyle != LJHOLD_TO_NEXT) { 46.529 + temp = p->holdPiece; 46.530 + p->holdPiece = p->curPiece[ihs]; 46.531 + } else { 46.532 + temp = p->curPiece[ihs + 1]; 46.533 + p->curPiece[ihs + 1] = p->curPiece[ihs]; 46.534 + } 46.535 + p->curPiece[ihs] = temp; 46.536 + p->alreadyHeld = 1; 46.537 + p->sounds |= LJSND_HOLD; 46.538 + 46.539 + // If a negative number was swapped into the current piece, 46.540 + // then there was nothing in the hold space (e.g. at the 46.541 + // beginning of a round). In this case, we'll need to fall 46.542 + // through and generate a new piece. 46.543 + if (temp >= 0) { 46.544 + rotateNewPiece(p); 46.545 + return changed; 46.546 + } 46.547 + } 46.548 + 46.549 + // Shift the next pieces down 46.550 + for (int i = 0; i < LJ_NEXT_PIECES; i++) { 46.551 + p->curPiece[i] = p->curPiece[i + 1]; 46.552 + } 46.553 + p->sounds |= LJSND_SPAWN; 46.554 + 46.555 + p->curPiece[LJ_NEXT_PIECES] = randomize(p); 46.556 + ++p->nPieces; 46.557 + if (!p->canRotate) { 46.558 + p->theta = (ljRand(p) >> 12) & 0x03; 46.559 + } else { 46.560 + rotateNewPiece(p); 46.561 + } 46.562 + 46.563 + return changed; 46.564 +} 46.565 + 46.566 +void newGame(LJField *p) { 46.567 + 46.568 + // Clear playfield 46.569 + for (int y = 0; y < LJ_PF_HT; y++) { 46.570 + for(int x = 0; x < LJ_PF_WID; x++) { 46.571 + p->b[y][x] = 0; 46.572 + } 46.573 + } 46.574 + 46.575 + for (int y = 0; y < LJ_MAX_LINES_PER_PIECE; ++y) { 46.576 + p->nLineClears[y] = 0; 46.577 + } 46.578 + 46.579 + if (p->holdStyle == LJHOLD_TNT) { 46.580 + initRandomize(p); 46.581 + p->holdPiece = randomize(p); 46.582 + } else { 46.583 + p->holdPiece = -1; // sentinel for no piece in hold box 46.584 + } 46.585 + 46.586 + // Generate pieces 46.587 + initRandomize(p); 46.588 + for(int i = 0; i < LJ_NEXT_PIECES; i++) { 46.589 + newPiece(p, 0); 46.590 + } 46.591 + p->clearedLines = 0; 46.592 + p->nPieces = 0; 46.593 + p->state = LJS_NEW_PIECE; 46.594 + p->stateTime = 1; 46.595 + p->garbage = 0; 46.596 + p->outGarbage = 0; 46.597 + p->score = 0; 46.598 + p->gameTime = 0; 46.599 + p->activeTime = 0; 46.600 + p->lines = 0; 46.601 + p->alreadyHeld = 0; 46.602 + p->chain = 0; 46.603 + p->theta = 0; 46.604 + p->nLinesThisPiece = 0; 46.605 + p->canRotate = 1; 46.606 + p->speed.entryDelay = 0; 46.607 + p->garbageRandomness = 64; 46.608 + p->reloaded = 0; 46.609 + p->monosquares = 0; 46.610 + p->multisquares = 0; 46.611 + 46.612 + p->garbageX = ljRand(p) % (p->rightWall - p->leftWall) 46.613 + + p->leftWall; 46.614 +} 46.615 + 46.616 +/** 46.617 + * Handles scoring for hard and soft drops. 46.618 + * @return 0 for no change or LJ_DIRTY_SCORE for change 46.619 + */ 46.620 +LJBits scoreDropRows(LJField *p, LJFixed gravity, LJFixed newY) { 46.621 + LJBits changed = 0; 46.622 + if (gravity > 0) { 46.623 + int fallDist = ljfixfloor(p->y) - ljfixfloor(newY); 46.624 + 46.625 + p->dropDist += fallDist; 46.626 + 46.627 + // Double scoring for hard drop 46.628 + if (p->dropScoreStyle == LJDROP_1S_2H 46.629 + && gravity >= ljitofix(p->ceiling)) { 46.630 + fallDist *= 2; 46.631 + } 46.632 + if (p->dropScoreStyle == LJDROP_1CELL 46.633 + || p->dropScoreStyle == LJDROP_1S_2H) { 46.634 + p->score += fallDist; 46.635 + changed |= LJ_DIRTY_SCORE; 46.636 + } 46.637 + 46.638 + // Handle scoring for continuous drop 46.639 + if (p->dropScoreStyle == LJDROP_NES 46.640 + && newY <= ljitofix(p->hardDropY)) { 46.641 + p->score += p->dropDist; 46.642 + changed |= LJ_DIRTY_SCORE; 46.643 + p->dropDist = 0; 46.644 + } 46.645 + } else { 46.646 + p->dropDist = 0; 46.647 + } 46.648 + return changed; 46.649 +} 46.650 + 46.651 +/** 46.652 + * Handles gravity. 46.653 + * @param p the playfield 46.654 + * @param gravity amount of additional gravity applied by the player 46.655 + * @param otherKeys other LJI_* keys being pressed by the player 46.656 + * (specifically LJI_LOCK) 46.657 + */ 46.658 +static LJBits doPieceGravity(LJField *p, LJFixed gravity, LJBits otherKeys) { 46.659 + int changedRows = 0; 46.660 + 46.661 + LJFixed newY = p->y - gravity - p->speed.gravity; 46.662 + 46.663 + // Check for landed 46.664 + if (newY <= ljitofix(p->hardDropY)) { 46.665 + newY = ljitofix(p->hardDropY); 46.666 + 46.667 + // Downward movement does not result in a T-spin 46.668 + if (ljfixfloor(newY) < ljfixfloor(p->y)) { 46.669 + p->isSpin = 0; 46.670 + } 46.671 + 46.672 + changedRows |= scoreDropRows(p, gravity, newY); 46.673 + p->y = newY; 46.674 + 46.675 + if (p->state == LJS_FALLING) { 46.676 + p->state = LJS_LANDED; 46.677 + p->stateTime = p->speed.lockDelay; 46.678 + p->sounds |= LJSND_LAND; 46.679 + } 46.680 + if (p->stateTime > 0 && !(otherKeys & LJI_LOCK)) { 46.681 + // lock delay > 128 is a special case for don't lock at all 46.682 + if (p->setLockDelay < 128) { 46.683 + --p->stateTime; 46.684 + } 46.685 + } else { 46.686 + LJBits lockRows = lockPiece(p); 46.687 + p->state = LJS_LINES; 46.688 + p->stateTime = 0; 46.689 + changedRows |= lockRows | LJ_DIRTY_NEXT; 46.690 + 46.691 + // LOCK OUT rule: If a piece locks 46.692 + // completely above the ceiling, the game is over. 46.693 + if (!(lockRows & ((1 << p->ceiling) - 1))) { 46.694 + p->state = LJS_GAMEOVER; 46.695 + } 46.696 + } 46.697 + } else { 46.698 + changedRows |= scoreDropRows(p, gravity, newY); 46.699 + p->state = LJS_FALLING; 46.700 + 46.701 + // Downward movement does not result in a T-spin 46.702 + if (ljfixfloor(newY) < ljfixfloor(p->y)) { 46.703 + p->isSpin = 0; 46.704 + } 46.705 + } 46.706 + p->y = newY; 46.707 + return changedRows; 46.708 +} 46.709 + 46.710 +static void updateLockDelayOnMove(LJField *p, int isUpwardKick) { 46.711 + if (p->state == LJS_LANDED) { 46.712 + 46.713 + // if tetromino can move down, go back to falling state; 46.714 + // otherwise, reset lock delay. 46.715 + if (!isCollision(p, p->x, ljfixfloor(p->y) - 1, p->theta)) { 46.716 + p->state = LJS_FALLING; 46.717 + if (p->lockReset == LJLOCK_SPAWN) { 46.718 + p->speed.lockDelay = p->stateTime; 46.719 + } 46.720 + p->stateTime = 0; 46.721 + } else { 46.722 + p->state = LJS_LANDED; 46.723 + if (p->lockReset == LJLOCK_MOVE 46.724 + || (p->lockReset == LJLOCK_STEP && isUpwardKick)) { 46.725 + p->stateTime = p->speed.lockDelay; 46.726 + } 46.727 + } 46.728 + } 46.729 +} 46.730 + 46.731 +static int doRotate(LJField *p, 46.732 + int newDir, 46.733 + const WallKickTable *const pieceTable, 46.734 + int withKicks) { 46.735 + int baseX = p->x; 46.736 + int baseY = ljfixfloor(p->y); 46.737 + 46.738 + withKicks = withKicks ? KICK_TABLE_LEN : 1; 46.739 + 46.740 + // allow specifying null tables for O 46.741 + if (!pieceTable) { 46.742 + if (!isCollision(p, baseX, baseY, newDir)) { 46.743 + p->theta = newDir; 46.744 + p->sounds |= LJSND_ROTATE; 46.745 + return 1; 46.746 + } 46.747 + return 0; 46.748 + } 46.749 + 46.750 + const unsigned char *const table = (*pieceTable)[newDir]; 46.751 + int baseKickY = -1000; // sentinel for uninitialized 46.752 + 46.753 + for (int kick = 0; kick < withKicks; kick++) { 46.754 + unsigned int kickData = table[kick]; 46.755 + if (kickData == WK_END) { 46.756 + break; 46.757 + } else if (kickData == ARIKA_IF_NOT_CENTER) { 46.758 + 46.759 + // Compute the free space position 46.760 + kickData = table[0]; 46.761 + int kickX = WKX(kickData) + baseX; 46.762 + int kickY = WKY(kickData) + baseY; 46.763 + LJBlkSpec blocks[4]; 46.764 + int allowed = 0; 46.765 + 46.766 + // If a block other than the center column of this position 46.767 + // is occupied, go to the next step (that is, 46.768 + // allow subsequent kicks) 46.769 + expandPieceToBlocks(blocks, p, p->curPiece[0], 46.770 + kickX, kickY, newDir); 46.771 + 46.772 + for (int blk = 0; blk < 4; ++blk) { 46.773 + if (blocks[blk].conn 46.774 + && blocks[blk].x != baseX + 1 46.775 + && isOccupied(p, blocks[blk].x, blocks[blk].y)) { 46.776 + allowed = 1; 46.777 + break; 46.778 + } 46.779 + } 46.780 + 46.781 + // Otherwise, only blocks of the center column are occupied, 46.782 + // and these cannot kick the piece. 46.783 + if (!allowed) { 46.784 + return 0; 46.785 + } 46.786 + } else { 46.787 + int kickX = WKX(kickData) + baseX; 46.788 + int kickY = WKY(kickData) + baseY; 46.789 + 46.790 + // If this is the first 46.791 + if (baseKickY == -1000) { 46.792 + baseKickY = kickY; 46.793 + } 46.794 + if ((kickY <= baseKickY || p->upwardKicks < p->maxUpwardKicks) 46.795 + && !isCollision(p, kickX, kickY, newDir)) { 46.796 + p->theta = newDir; 46.797 + p->x = kickX; 46.798 + if (kickY > baseKickY) { 46.799 + p->y = ljitofix(kickY); 46.800 + 46.801 + // on the FIRST floor kick of a piece, reset lock delay 46.802 + if (p->upwardKicks == 0) { 46.803 + updateLockDelayOnMove(p, 1); 46.804 + } 46.805 + ++p->upwardKicks; 46.806 + } else if (kickY < baseKickY) { 46.807 + p->y = ljitofix(kickY) + 0xFFFF; 46.808 + } else { 46.809 + p->y = ljitofix(kickY) + (p->y & 0xFFFF); 46.810 + } 46.811 + p->sounds |= LJSND_ROTATE; 46.812 + return 1; 46.813 + } 46.814 + } 46.815 + } 46.816 + return 0; 46.817 +} 46.818 + 46.819 + 46.820 +/** 46.821 + * Tries to rotate the current piece 90 degrees counterclockwise, 46.822 + * using the counterclockwise wall kick tables. 46.823 + * @param p the playfield 46.824 + * @param withKicks nonzero for wall kick, zero for none 46.825 + */ 46.826 +static int doRotateLeft(LJField *p, int withKicks) { 46.827 + int newDir = (p->theta + 3) & 0x03; 46.828 + const LJRotSystem *rs = rotSystems[p->rotationSystem]; 46.829 + signed int tableNo = rs->kicksL[p->curPiece[0] & LJP_MASK]; 46.830 + const WallKickTable *pieceTable = tableNo >= 0 46.831 + ? &(rs->kickTables[tableNo]) 46.832 + : NULL; 46.833 + return doRotate(p, newDir, pieceTable, withKicks); 46.834 +} 46.835 + 46.836 +/** 46.837 + * Tries to rotate the current piece 90 degrees clockwise, 46.838 + * using the clockwise wall kick tables. 46.839 + * @param p the playfield 46.840 + * @param withKicks nonzero for wall kick, zero for none 46.841 + */ 46.842 +static int doRotateRight(LJField *p, int withKicks) { 46.843 + int newDir = (p->theta + 1) & 0x03; 46.844 + const LJRotSystem *rs = rotSystems[p->rotationSystem]; 46.845 + signed int tableNo = rs->kicksR[p->curPiece[0] & LJP_MASK]; 46.846 + const WallKickTable *pieceTable = tableNo >= 0 46.847 + ? &(rs->kickTables[tableNo]) 46.848 + : NULL; 46.849 + return doRotate(p, newDir, pieceTable, withKicks); 46.850 +} 46.851 + 46.852 +static LJBits checkLines(const LJField *p, LJBits checkRows) { 46.853 + LJBits foundLines = 0; 46.854 + for (int y = 0; 46.855 + y < LJ_PF_HT && checkRows != 0; 46.856 + ++y, checkRows >>= 1) { 46.857 + if (checkRows & 1) { 46.858 + const unsigned char *row = p->b[y]; 46.859 + int found = 1; 46.860 + 46.861 + for (int x = p->leftWall; x < p->rightWall && found; ++x) { 46.862 + found = found && (row[x] != 0); 46.863 + } 46.864 + if (found) { 46.865 + foundLines |= 1 << y; 46.866 + } 46.867 + } 46.868 + } 46.869 + 46.870 + return foundLines; 46.871 +} 46.872 + 46.873 +static void fillCLoop(LJField *p, int x, int y, unsigned int src, unsigned int dst) 46.874 +{ 46.875 + int fillL, fillR, i; 46.876 + 46.877 + fillL = fillR = x; 46.878 + do { 46.879 + p->c[y][fillL] = dst; 46.880 + fillL--; 46.881 + } while ((fillL >= p->leftWall) && (p->c[y][fillL] == src)); 46.882 + fillL++; 46.883 + 46.884 + do { 46.885 + p->c[y][fillR] = dst; 46.886 + fillR++; 46.887 + } while ((fillR < p->rightWall) && (p->c[y][fillR] == src)); 46.888 + fillR--; 46.889 + 46.890 + for(i = fillL; i <= fillR; i++) 46.891 + { 46.892 + if(y > 0 && p->c[y - 1][i] == src) { 46.893 + fillCLoop(p, i, y - 1, src, dst); 46.894 + } 46.895 + if(y < LJ_PF_HT - 1 && p->c[y + 1][i] == src) { 46.896 + fillCLoop(p, i, y + 1, src, dst); 46.897 + } 46.898 + } 46.899 +} 46.900 + 46.901 + 46.902 +static void fillC(LJField *p, int x, int y, int dstC) { 46.903 + if (p->c[y][x] != dstC) { 46.904 + fillCLoop(p, x, y, p->c[y][x], dstC); 46.905 + } 46.906 +} 46.907 + 46.908 +/** 46.909 + * Locks the block regions that have landed. 46.910 + * @param p the playfield 46.911 + */ 46.912 +void lockLandedRegions(LJField *p) { 46.913 + // Look for regions that are on top of ground regions, where 46.914 + // "ground regions" are any block that is solid and whose region ID is 0. 46.915 + for (int landed = 1; landed != 0; ) { 46.916 + landed = 0; 46.917 + // If something hit the ground, erase its floating bit 46.918 + for (int y = 0; y < LJ_PF_HT; ++y) { 46.919 + for (int x = p->leftWall; x < p->rightWall; ++x) { 46.920 + // If there's a floating block here, and a not-floating block below, 46.921 + // erase this block group's floatiness 46.922 + if (p->c[y][x] && 46.923 + (y == 0 || (!p->c[y - 1][x] && p->b[y - 1][x]))) { 46.924 + fillC(p, x, y, 0); 46.925 + p->sounds |= LJSND_LAND; 46.926 + landed = 1; 46.927 + } 46.928 + } 46.929 + } 46.930 + } 46.931 +} 46.932 + 46.933 +/** 46.934 + * Separates the playfield into regions that shall fall separately. 46.935 + * @param p the playfield 46.936 + * @param byColors Zero: Touching blocks form a region. 46.937 + * Nonzero: Touching blocks of a single color form a region. 46.938 + */ 46.939 +static void stickyMark(LJField *p, int byColors) { 46.940 + for (unsigned int y = 0; y < LJ_PF_HT; ++y) { 46.941 + for (unsigned int x = p->leftWall; x < p->rightWall; ++x) { 46.942 + int blkHere = p->b[y][x] & COLOR_MASK; 46.943 + 46.944 + if (!byColors) { 46.945 + blkHere = blkHere ? 0x10 : 0; 46.946 + } 46.947 + p->c[y][x] = blkHere; 46.948 + } 46.949 + } 46.950 + 46.951 + if (byColors) { 46.952 + lockLandedRegions(p); 46.953 + } else { 46.954 + // mark the bottom row as landed 46.955 + for (unsigned int x = p->leftWall; x < p->rightWall; ++x) { 46.956 + if (p->c[0][x]) { 46.957 + fillC(p, x, 0, 0); 46.958 + } 46.959 + } 46.960 + } 46.961 + 46.962 + //p->stateTime = 5; 46.963 +} 46.964 + 46.965 + 46.966 +/** 46.967 + * Sets the color of a piece to gray/garbage (0x80). 46.968 + * @param x column of a block in the piece 46.969 + * @param y row of a block in the piece 46.970 + * @param rgn the region ID 46.971 + */ 46.972 +static void cascadeMarkPiece(LJField *p, int x, int y, int rgn) { 46.973 + int blkHere = p->b[y][x]; 46.974 + 46.975 + if (blkHere && !p->c[y][x]) { 46.976 + p->c[y][x] = rgn; 46.977 + if((blkHere & CONNECT_D) && y > 0) 46.978 + cascadeMarkPiece(p, x, y - 1, rgn); 46.979 + if((blkHere & CONNECT_U) && y < LJ_PF_HT - 1) 46.980 + cascadeMarkPiece(p, x, y + 1, rgn); 46.981 + if((blkHere & CONNECT_L) && x > p->leftWall) 46.982 + cascadeMarkPiece(p, x - 1, y, rgn); 46.983 + if((blkHere & CONNECT_R) && x < p->rightWall - 1 ) 46.984 + cascadeMarkPiece(p, x + 1, y, rgn); 46.985 + } 46.986 +} 46.987 + 46.988 +static void cascadeMark(LJField *p) { 46.989 + int rgn = 0; 46.990 + 46.991 + for (unsigned int y = 0; y < LJ_PF_HT; ++y) { 46.992 + for (unsigned int x = p->leftWall; x < p->rightWall; ++x) { 46.993 + p->c[y][x] = 0; 46.994 + } 46.995 + } 46.996 + for (unsigned int y = 0; y < LJ_PF_HT; ++y) { 46.997 + for (unsigned int x = p->leftWall; x < p->rightWall; ++x) { 46.998 + cascadeMarkPiece(p, x, y, ++rgn); 46.999 + } 46.1000 + } 46.1001 + lockLandedRegions(p); 46.1002 + //p->stateTime = 5; 46.1003 +} 46.1004 + 46.1005 +static void breakEverything(LJField *p) { 46.1006 + for (unsigned int y = 0; y < LJ_PF_HT; ++y) { 46.1007 + for (unsigned int x = p->leftWall; x < p->rightWall; ++x) { 46.1008 + if (p->b[y][x]) { 46.1009 + p->c[y][x] = x + 1; 46.1010 + p->b[y][x] = 0x80; 46.1011 + } else { 46.1012 + p->c[y][x] = 0; 46.1013 + } 46.1014 + } 46.1015 + } 46.1016 + 46.1017 + // fill bottom row 46.1018 + for (unsigned int x = x = p->leftWall; x < p->rightWall; ++x) { 46.1019 + if (p->c[0][x]) { 46.1020 + fillC(p, x, 0, 0); 46.1021 + } 46.1022 + } 46.1023 + p->stateTime = 5; 46.1024 +} 46.1025 + 46.1026 +/** 46.1027 + * Sets the color of a piece to gray/garbage (0x80). 46.1028 + * @param x column of a block in the piece 46.1029 + * @param y row of a block in the piece 46.1030 + */ 46.1031 +static LJBits breakPiece(LJField *p, int x, int y) { 46.1032 + LJBits changed = 0; 46.1033 + int blkHere = p->b[y][x]; 46.1034 + int colorHere = blkHere & COLOR_MASK; 46.1035 + int connHere = blkHere & CONNECT_MASK; 46.1036 + 46.1037 + if (colorHere != 0x80) { 46.1038 + p->b[y][x] = connHere | 0x80; 46.1039 + changed |= 1 << y; 46.1040 + if((blkHere & CONNECT_D) && y > 0) 46.1041 + changed |= breakPiece(p, x, y - 1); 46.1042 + if((blkHere & CONNECT_U) && y < LJ_PF_HT - 1) 46.1043 + changed |= breakPiece(p, x, y + 1); 46.1044 + if((blkHere & CONNECT_L) && x > p->leftWall) 46.1045 + changed |= breakPiece(p, x - 1, y); 46.1046 + if((blkHere & CONNECT_R) && x < p->rightWall - 1 ) 46.1047 + changed |= breakPiece(p, x + 1, y); 46.1048 + } 46.1049 + return changed; 46.1050 +} 46.1051 + 46.1052 +/** 46.1053 + * Removes blocks in cleared lines from the playfield and marks 46.1054 + * remaining blocks for gravity. 46.1055 + * @param the lines to be cleared 46.1056 + * @return the rows that were changed 46.1057 + */ 46.1058 +static LJBits clearLines(LJField *p, LJBits foundLines) { 46.1059 + LJBits changed = foundLines; 46.1060 + 46.1061 + p->clearedLines = foundLines; 46.1062 + if (foundLines != 0) { 46.1063 + p->sounds |= LJSND_LINE; 46.1064 + } 46.1065 + for (int y = 0; 46.1066 + y < LJ_PF_HT && foundLines != 0; 46.1067 + ++y, foundLines >>= 1) { 46.1068 + if (foundLines & 1) { 46.1069 + 46.1070 + // In square mode, turn broken pieces (but not 4x4 squares) 46.1071 + // into garbage blocks 46.1072 + if (p->gluing == LJGLUING_SQUARE) { 46.1073 + for (int x = p->leftWall; x < p->rightWall; ++x) { 46.1074 + if (p->b[y][x] < 0x80) { 46.1075 + changed |= breakPiece(p, x, y); 46.1076 + } else if ((p->b[y][x] & (0xF0 | CONNECT_R)) == 0xA0) { 46.1077 + p->score += 500; 46.1078 + changed |= LJ_DIRTY_SCORE; 46.1079 + } else if ((p->b[y][x] & (0xF0 | CONNECT_R)) == 0xB0) { 46.1080 + p->score += 1000; 46.1081 + changed |= LJ_DIRTY_SCORE; 46.1082 + } 46.1083 + } 46.1084 + } 46.1085 + 46.1086 + for (int x = p->leftWall; x < p->rightWall; ++x) { 46.1087 + p->b[y][x] = 0; 46.1088 + } 46.1089 + 46.1090 + // break connections up and down (like Tengen Tetyais) 46.1091 + if (y > 0) { 46.1092 + for (int x = p->leftWall; x < p->rightWall; ++x) { 46.1093 + p->b[y - 1][x] &= ~CONNECT_U; 46.1094 + } 46.1095 + changed |= 1 << (y - 1); 46.1096 + } 46.1097 + if (y < LJ_PF_HT - 1) { 46.1098 + for (int x = p->leftWall; x < p->rightWall; ++x) { 46.1099 + p->b[y + 1][x] &= ~CONNECT_D; 46.1100 + } 46.1101 + changed |= 1 << (y + 1); 46.1102 + } 46.1103 + } 46.1104 + } 46.1105 + if (p->gluing == LJGLUING_SQUARE && p->isSpin) { 46.1106 + breakEverything(p); 46.1107 + changed |= (1 << LJ_PF_HT) - 1; 46.1108 + } else if (p->clearGravity == LJGRAV_STICKY) { 46.1109 + stickyMark(p, 0); 46.1110 + } else if (p->clearGravity == LJGRAV_STICKY_BY_COLOR) { 46.1111 + stickyMark(p, 1); 46.1112 + } else if (p->clearGravity == LJGRAV_CASCADE) { 46.1113 + cascadeMark(p); 46.1114 + } else { 46.1115 + p->stateTime = 0; 46.1116 + } 46.1117 + 46.1118 + return changed; 46.1119 +} 46.1120 + 46.1121 +static unsigned int stickyFallLines(LJField *p) { 46.1122 + int minY = LJ_PF_HT; 46.1123 + 46.1124 + // Move floating stuff down by one block 46.1125 + for (int y = 1; y < LJ_PF_HT; ++y) { 46.1126 + for (int x = p->leftWall; x < p->rightWall; ++x) { 46.1127 + int c = p->c[y][x]; 46.1128 + if (c) { 46.1129 + p->c[y - 1][x] = c; 46.1130 + p->c[y][x] = 0; 46.1131 + p->b[y - 1][x] = p->b[y][x]; 46.1132 + p->b[y][x] = 0; 46.1133 + 46.1134 + if (minY > y) { 46.1135 + minY = y; 46.1136 + } 46.1137 + } 46.1138 + } 46.1139 + } 46.1140 + 46.1141 + // If we're done, skip all the rest 46.1142 + if (minY >= LJ_PF_HT) { 46.1143 + return LJ_PF_HT; 46.1144 + } 46.1145 + 46.1146 + lockLandedRegions(p); 46.1147 + return minY - 1; 46.1148 +} 46.1149 + 46.1150 + 46.1151 +unsigned int bfffo(LJBits rowBits) { 46.1152 + unsigned int lineRow = 0; 46.1153 + 46.1154 + if (!rowBits) { 46.1155 + return 32; 46.1156 + } 46.1157 + if ((rowBits & 0xFFFF) == 0) { 46.1158 + rowBits >>= 16; 46.1159 + lineRow += 16; 46.1160 + } 46.1161 + if ((rowBits & 0xFF) == 0) { 46.1162 + rowBits >>= 8; 46.1163 + lineRow += 8; 46.1164 + } 46.1165 + if ((rowBits & 0xF) == 0) { 46.1166 + rowBits >>= 4; 46.1167 + lineRow += 4; 46.1168 + } 46.1169 + if ((rowBits & 0x3) == 0) { 46.1170 + rowBits >>= 2; 46.1171 + lineRow += 2; 46.1172 + } 46.1173 + if ((rowBits & 0x1) == 0) { 46.1174 + rowBits >>= 1; 46.1175 + lineRow += 1; 46.1176 + } 46.1177 + return lineRow; 46.1178 +} 46.1179 + 46.1180 +static unsigned int fallLines(LJField *p) { 46.1181 + LJBits rowBits = p->tempRows; 46.1182 + unsigned int lineRow = 0; 46.1183 + 46.1184 + if (p->clearGravity != LJGRAV_NAIVE 46.1185 + || (p->gluing == LJGLUING_SQUARE && p->isSpin)) { 46.1186 + return stickyFallLines(p); 46.1187 + } 46.1188 + 46.1189 + if (rowBits == 0) { 46.1190 + return LJ_PF_HT; 46.1191 + } 46.1192 + 46.1193 + lineRow = bfffo(rowBits); 46.1194 + p->tempRows = (p->tempRows & (-2 << lineRow)) >> 1; 46.1195 + if (!(p->tempRows & (1 << lineRow))) { 46.1196 + p->sounds |= LJSND_LAND; 46.1197 + } 46.1198 + 46.1199 + // Move stuff down by 1 row 46.1200 + for (int y = lineRow; y < LJ_PF_HT - 1; ++y) { 46.1201 + unsigned char *row0 = p->b[y]; 46.1202 + const unsigned char *row1 = p->b[y + 1]; 46.1203 + for (int x = p->leftWall; x < p->rightWall; ++x) { 46.1204 + row0[x] = row1[x]; 46.1205 + } 46.1206 + } 46.1207 + 46.1208 + // Clear top row 46.1209 + for (int x = p->leftWall; x < p->rightWall; ++x) { 46.1210 + p->b[LJ_PF_HT - 1][x] = 0; 46.1211 + } 46.1212 + 46.1213 + return lineRow; 46.1214 +} 46.1215 + 46.1216 +/** 46.1217 + * Counts the bits in a bit vector that are true (1). 46.1218 + * @param b a bit vector 46.1219 + * @return the number of 1 bits 46.1220 + */ 46.1221 +unsigned int countOnes(LJBits b) { 46.1222 + unsigned int ones = 0; 46.1223 + 46.1224 + while(b) { 46.1225 + ++ones; 46.1226 + b &= b - 1; 46.1227 + } 46.1228 + return ones; 46.1229 +} 46.1230 + 46.1231 +static unsigned int addGarbage(LJField *p) { 46.1232 + // Move stuff up by 1 row 46.1233 + for (int y = LJ_PF_HT - 2; y >= 0; --y) { 46.1234 + unsigned char *row1 = p->b[y + 1]; 46.1235 + const unsigned char *row0 = p->b[y]; 46.1236 + for (int x = p->leftWall; x < p->rightWall; ++x) { 46.1237 + row1[x] = row0[x]; 46.1238 + } 46.1239 + } 46.1240 + 46.1241 + // Garbage in bottom row 46.1242 + for (int x = p->leftWall; x < p->rightWall; ++x) { 46.1243 + p->b[0][x] = 0x80; 46.1244 + } 46.1245 + 46.1246 + // Randomize location of garbage hole 46.1247 + int r = (ljRand(p) >> 7) & 0xFF; 46.1248 + int garbageX = (r <= p->garbageRandomness) 46.1249 + ? (ljRand(p) % (p->rightWall - p->leftWall)) + p->leftWall 46.1250 + : p->garbageX; 46.1251 + p->b[0][garbageX] = 0; 46.1252 + p->garbageX = garbageX; 46.1253 + 46.1254 + // Horizontally connect the blocks that make up garbage in bottom row 46.1255 + for (int x = p->leftWall; x < p->rightWall - 1; ++x) { 46.1256 + if (p->b[0][x] && p->b[0][x + 1]) { 46.1257 + p->b[0][x] |= CONNECT_R; 46.1258 + p->b[0][x + 1] |= CONNECT_L; 46.1259 + } 46.1260 + } 46.1261 + 46.1262 + // Vertically connect the blocks that make up garbage in bottom row 46.1263 + for (int x = p->leftWall; x < p->rightWall; ++x) { 46.1264 + if (p->b[0][x] 46.1265 + && ((p->b[1][x] & COLOR_MASK) == 0x80)) { 46.1266 + p->b[0][x] |= CONNECT_U; 46.1267 + p->b[1][x] |= CONNECT_D; 46.1268 + } 46.1269 + } 46.1270 + 46.1271 + return (1 << LJ_PF_VIS_HT) - 1; 46.1272 +} 46.1273 + 46.1274 +/** 46.1275 + * Computes the score and outgoing garbage for lines 46.1276 + * and adds them to the player's total. 46.1277 + * @param p The playfield 46.1278 + * @param lines Bit array where 1 means a line clear on this row. 46.1279 + */ 46.1280 +void addLinesScore(LJField *p, LJBits lines); 46.1281 + 46.1282 +/** 46.1283 + * Things to do just before launching a new piece. 46.1284 + */ 46.1285 +void prepareForNewPiece(LJField *p) { 46.1286 + int nLines = p->nLinesThisPiece; 46.1287 + 46.1288 + // Add to number of clears of each number of lines. 46.1289 + int idx; 46.1290 + 46.1291 + if (p->clearGravity == LJGRAV_NAIVE) { 46.1292 + // In naive gravity, T-spin single, double, and triple counts 46.1293 + // are stored in 5-row, 6-row, and 7-row slots, and T-spins 46.1294 + // that clear 0 lines are not counted. 46.1295 + idx = (nLines > 4) 46.1296 + ? 4 46.1297 + : nLines; 46.1298 + 46.1299 + if (nLines >= 1 && p->isSpin) { 46.1300 + idx += 4; 46.1301 + } 46.1302 + } else { 46.1303 + idx = (nLines > LJ_MAX_LINES_PER_PIECE) 46.1304 + ? LJ_MAX_LINES_PER_PIECE 46.1305 + : nLines; 46.1306 + } 46.1307 + 46.1308 + if (nLines < 4 && !p->isSpin && p->garbageStyle == LJGARBAGE_HRDERBY) { 46.1309 + p->garbage += nLines; 46.1310 + } 46.1311 + 46.1312 + ljAssert(p, 46.1313 + idx <= LJ_MAX_LINES_PER_PIECE, 46.1314 + "Number of lines cleared with last piece out of bounds in prepareForNewPiece"); 46.1315 + if (idx > 0) { 46.1316 + p->nLineClears[idx - 1] += 1; 46.1317 + } 46.1318 + 46.1319 + p->state = LJS_NEW_PIECE; 46.1320 + p->stateTime = p->speed.entryDelay; 46.1321 +} 46.1322 + 46.1323 +LJBits frame(LJField *p, const LJInput *in) { 46.1324 + LJBits changedRows = 0; 46.1325 + LJBits tempRows; 46.1326 + int distance; 46.1327 + int moved = 0; 46.1328 + int isFirstFrame = (p->sounds & (LJSND_SPAWN | LJSND_HOLD)) 46.1329 + ? 1 : 0; 46.1330 + 46.1331 + p->sounds = 0; 46.1332 + 46.1333 + // Make hold work at ANY time. 46.1334 + if ((in->other & LJI_HOLD) 46.1335 + && p->holdStyle != LJHOLD_NONE 46.1336 + && !p->alreadyHeld) { 46.1337 + changedRows |= newPiece(p, 1) | LJ_DIRTY_NEXT; 46.1338 + updHardDropY(p); 46.1339 + } 46.1340 + 46.1341 + switch(p->state) { 46.1342 + case LJS_NEW_PIECE: 46.1343 + if (p->garbage > 0) { 46.1344 + changedRows |= addGarbage(p); 46.1345 + --p->garbage; 46.1346 + break; 46.1347 + } 46.1348 + 46.1349 + // ARE 46.1350 + if (p->stateTime > 0) { 46.1351 + --p->stateTime; 46.1352 + } 46.1353 + if (p->stateTime > 0) { 46.1354 + break; 46.1355 + } 46.1356 + 46.1357 + changedRows |= newPiece(p, 0); 46.1358 + updHardDropY(p); 46.1359 + changedRows |= LJ_DIRTY_NEXT; 46.1360 + 46.1361 + /* If the piece spawns over blocks, this is a "block out" and a 46.1362 + loss under most rules. But skip checking it now so that 46.1363 + the player can IRS out of block out. */ 46.1364 + 46.1365 + break; 46.1366 + 46.1367 + // the following executes for both falling and landed 46.1368 + case LJS_FALLING: 46.1369 + case LJS_LANDED: 46.1370 + ++p->activeTime; 46.1371 + if (p->canRotate) { 46.1372 + int oldX = p->x; 46.1373 + int oldY = ljfixfloor(p->y); 46.1374 + distance = in->rotation; 46.1375 + for(; distance < 0; ++distance) { 46.1376 + 46.1377 + // 0.43: Do not apply wall kicks on the first frame (IRS) 46.1378 + if (doRotateLeft(p, !isFirstFrame)) { 46.1379 + moved = 1; 46.1380 + 46.1381 + // isSpin == 1: twist in place 46.1382 + // isSpin == 2: twist with kick 46.1383 + // if (p->tSpinAlgo == LJTS_TDS_NO_KICK) 46.1384 + // then only isSpin == 1 is worth points. 46.1385 + if (p->x == oldX && ljfixfloor(p->y) == oldY) { 46.1386 + p->isSpin = 1; 46.1387 + } else { 46.1388 + p->isSpin = 2; 46.1389 + } 46.1390 + } else { 46.1391 + break; 46.1392 + } 46.1393 + } 46.1394 + for(; distance > 0; --distance) { 46.1395 + if (doRotateRight(p, !isFirstFrame)) { 46.1396 + moved = 1; 46.1397 + if (p->x == oldX && ljfixfloor(p->y) == oldY) { 46.1398 + p->isSpin = 1; 46.1399 + } else { 46.1400 + p->isSpin = 2; 46.1401 + } 46.1402 + } else { 46.1403 + break; 46.1404 + } 46.1405 + } 46.1406 + } 46.1407 + 46.1408 + /* If the piece spawns over blocks, this is a "block out" and a 46.1409 + loss under most rules. Check it now, after rotation, so that 46.1410 + the player can IRS out of block out. */ 46.1411 + if (isFirstFrame 46.1412 + && isCollision(p, p->x, ljfixfloor(p->y), p->theta)) { 46.1413 + changedRows |= lockPiece(p); 46.1414 + p->state = LJS_GAMEOVER; 46.1415 + } 46.1416 + 46.1417 + distance = in->movement; 46.1418 + for(; distance < 0; ++distance) { 46.1419 + if (!isCollision(p, p->x - 1, ljfixfloor(p->y), p->theta)) { 46.1420 + --p->x; 46.1421 + p->sounds |= LJSND_SHIFT; 46.1422 + moved = 1; 46.1423 + p->isSpin = 0; 46.1424 + } 46.1425 + } 46.1426 + for(; distance > 0; --distance) { 46.1427 + if (!isCollision(p, p->x + 1, ljfixfloor(p->y), p->theta)) { 46.1428 + ++p->x; 46.1429 + p->sounds |= LJSND_SHIFT; 46.1430 + moved = 1; 46.1431 + p->isSpin = 0; 46.1432 + } 46.1433 + } 46.1434 + updHardDropY(p); 46.1435 + if (p->state != LJS_GAMEOVER) { 46.1436 + if (moved) { 46.1437 + updateLockDelayOnMove(p, 0); 46.1438 + } 46.1439 + tempRows = doPieceGravity(p, ljitofix(in->gravity) >> 3, in->other); 46.1440 + p->tempRows = tempRows; 46.1441 + changedRows |= tempRows; 46.1442 + } 46.1443 + 46.1444 + // At this point, if the piece locked, 46.1445 + // p->tempRows holds the rows in which the piece landed. 46.1446 + break; 46.1447 + 46.1448 + case LJS_LINES: 46.1449 + if (p->stateTime > 0) { 46.1450 + --p->stateTime; 46.1451 + } 46.1452 + if (p->stateTime > 0) { 46.1453 + break; 46.1454 + } 46.1455 + if (p->gluing == LJGLUING_SQUARE) { 46.1456 + LJBits gluedRows = findSquares(p, 0); 46.1457 + gluedRows |= findSquares(p, 1); 46.1458 + changedRows |= gluedRows; 46.1459 + if (gluedRows) { 46.1460 + 46.1461 + // When a 4x4 block square is formed, a delay 46.1462 + // equal to the line delay is added. 46.1463 + p->stateTime += p->speed.lineDelay; 46.1464 + break; 46.1465 + } 46.1466 + } else if (p->gluing == LJGLUING_STICKY 46.1467 + || p->gluing == LJGLUING_STICKY_BY_COLOR) { 46.1468 + changedRows |= stickyGluing(p); 46.1469 + } 46.1470 + 46.1471 + // At this point, p->tempRows holds the rows in which 46.1472 + // a line could possibly have been made. 46.1473 + tempRows = p->tempRows; 46.1474 + tempRows = checkLines(p, tempRows); 46.1475 + p->tempRows = tempRows; 46.1476 + // At this point, p->tempRows holds the rows in which 46.1477 + // a line HAS been made. 46.1478 + addLinesScore(p, tempRows); 46.1479 + changedRows |= LJ_DIRTY_SCORE; 46.1480 + 46.1481 + // At this point, p->tempRows holds the rows in which a line 46.1482 + // HAS been made. 46.1483 + p->clearedLines = tempRows; 46.1484 + if (!tempRows) { 46.1485 + prepareForNewPiece(p); 46.1486 + break; 46.1487 + } 46.1488 + 46.1489 + changedRows |= clearLines(p, tempRows); 46.1490 + 46.1491 + p->state = LJS_LINES_FALLING; 46.1492 + p->stateTime += p->speed.lineDelay; 46.1493 + break; 46.1494 + 46.1495 + case LJS_LINES_FALLING: 46.1496 + if (p->stateTime > 0) { 46.1497 + --p->stateTime; 46.1498 + } 46.1499 + if (p->stateTime > 0) { 46.1500 + break; 46.1501 + } 46.1502 + moved = fallLines(p); 46.1503 + if (moved >= LJ_PF_HT) { 46.1504 + p->state = LJS_LINES; 46.1505 + p->tempRows = (1 << LJ_PF_HT) - 1; 46.1506 + } 46.1507 + changedRows |= (~0 << moved) & ((1 << LJ_PF_VIS_HT) - 1); 46.1508 + break; 46.1509 + 46.1510 + default: 46.1511 + break; 46.1512 + 46.1513 + } 46.1514 + 46.1515 + ++p->gameTime; 46.1516 + return changedRows; 46.1517 +} 46.1518 + 46.1519 +
47.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 47.2 +++ b/src/lj.h Fri Mar 13 00:39:12 2009 -0700 47.3 @@ -0,0 +1,507 @@ 47.4 +/* Header file for the engine of LOCKJAW, an implementation of the Soviet Mind Game 47.5 + 47.6 +Copyright (C) 2006 Damian Yerrick <tepples+lj@spamcop.net> 47.7 + 47.8 +This work is free software; you can redistribute it and/or modify 47.9 +it under the terms of the GNU General Public License as published by 47.10 +the Free Software Foundation; either version 2 of the License, or 47.11 +(at your option) any later version. 47.12 + 47.13 +This program is distributed in the hope that it will be useful, 47.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 47.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 47.16 +GNU General Public License for more details. 47.17 + 47.18 +You should have received a copy of the GNU General Public License 47.19 +along with this program; if not, write to the Free Software 47.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 47.21 + 47.22 +Original game concept and design by Alexey Pajitnov. 47.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 47.24 +or The Tetris Company LLC. 47.25 + 47.26 +*/ 47.27 + 47.28 +#ifndef LJ_H 47.29 +#define LJ_H 47.30 + 47.31 +#include <stdlib.h> 47.32 +#include "ljtypes.h" 47.33 + 47.34 +enum { 47.35 + CONNECT_U = 0x01, 47.36 + CONNECT_R = 0x02, 47.37 + CONNECT_D = 0x04, 47.38 + CONNECT_L = 0x08, 47.39 + CONNECT_UD = CONNECT_U | CONNECT_D, 47.40 + CONNECT_UL = CONNECT_U | CONNECT_L, 47.41 + CONNECT_DL = CONNECT_D | CONNECT_L, 47.42 + CONNECT_UR = CONNECT_U | CONNECT_R, 47.43 + CONNECT_DR = CONNECT_D | CONNECT_R, 47.44 + CONNECT_LR = CONNECT_L | CONNECT_R, 47.45 + CONNECT_UDL = CONNECT_UD | CONNECT_L, 47.46 + CONNECT_UDR = CONNECT_UD | CONNECT_R, 47.47 + CONNECT_ULR = CONNECT_UL | CONNECT_R, 47.48 + CONNECT_DLR = CONNECT_DL | CONNECT_R, 47.49 + CONNECT_UDLR = CONNECT_UD | CONNECT_LR, 47.50 + CONNECT_MASK = CONNECT_UDLR, 47.51 + COLOR_MASK = 0xF0 47.52 +}; 47.53 + 47.54 +typedef unsigned char LJBlock; 47.55 + 47.56 +typedef enum LJState { 47.57 + LJS_INACTIVE, 47.58 + LJS_NEW_PIECE, /* delay is ARE */ 47.59 + LJS_FALLING, /* delay is fall delay; NOT IMPLEMENTED */ 47.60 + LJS_LANDED, /* delay is lock delay */ 47.61 + LJS_LINES, /* delay is line delay */ 47.62 + LJS_LINES_FALLING, /* delay is fall delay per line; NOT IMPLEMENTED */ 47.63 + LJS_GAMEOVER, /* game over animation */ 47.64 + LJS_EXPLODE /* used for bombliss extension */ 47.65 +} LJState; 47.66 + 47.67 +// for other 47.68 +enum { 47.69 + LJI_HOLD = 0x01, 47.70 + LJI_LOCK = 0x02 47.71 +}; 47.72 + 47.73 +// for dirty bits 47.74 +enum { 47.75 + LJ_DIRTY_NEXT = 0x01000000, 47.76 + LJ_DIRTY_SCORE = 0x02000000 47.77 +}; 47.78 + 47.79 +// for gimmick (game mode) 47.80 +enum { 47.81 + LJGM_ATYPE, // marathon 47.82 + LJGM_BTYPE, // 40 lines 47.83 + LJGM_ULTRA, // 180 seconds 47.84 + LJGM_DRILL, // clear bottom line 47.85 + LJGM_ITEMS, // no rotation + no next + fast drop + garbage + banana 47.86 + LJGM_BABY, // 300 keypresses 47.87 + LJGM_N_GIMMICKS 47.88 +}; 47.89 + 47.90 +enum { 47.91 + LJP_I = 0, 47.92 + LJP_J, 47.93 + LJP_L, 47.94 + LJP_O, 47.95 + LJP_S, 47.96 + LJP_T, 47.97 + LJP_Z, 47.98 + LJP_I2, 47.99 + LJP_I3, 47.100 + LJP_L3, 47.101 + LJP_GARBAGE = 7, 47.102 + LJP_MASK = 0x0F 47.103 +}; 47.104 + 47.105 +enum { 47.106 + LJSND_ROTATE = 0x0001, 47.107 + LJSND_SHIFT = 0x0002, 47.108 + LJSND_DROP = 0x0004, 47.109 + LJSND_LAND = 0x0008, 47.110 + LJSND_LOCK = 0x0010, 47.111 + LJSND_LINE = 0x0020, // a line was scored 47.112 + LJSND_SETB2B = 0x0040, // this line is b2b worthy (tetris or t-spin) 47.113 + LJSND_B2B = 0x0080, // this line AND last line were b2b worthy 47.114 + LJSND_SPAWN = 0x0100, // next piece was moved up 47.115 + LJSND_HOLD = 0x0200, // hold piece was activated 47.116 + LJSND_IRS = 0x0400, // initial rotation: spawn last frame and rotation this frame 47.117 + LJSND_SQUARE = 0x0800, // formed 4x4 square 47.118 + LJSND_COUNTDOWN = 0x4000, // countdown value changed 47.119 + LJSND_SECTIONUP = 0x8000, // section increased 47.120 +}; 47.121 + 47.122 +enum { 47.123 + LJRAND_PURE, 47.124 + LJRAND_BAG, 47.125 + LJRAND_BAGPLUS1, 47.126 + LJRAND_2BAG, 47.127 + LJRAND_HIST_INF, 47.128 + LJRAND_HIST_6, 47.129 + LJRAND_N_RANDS 47.130 +}; 47.131 + 47.132 +enum { 47.133 + LJRAND_4BLK, 47.134 + LJRAND_JLOSTZ, 47.135 + LJRAND_SZ, 47.136 + LJRAND_I, 47.137 + LJRAND_234BLK, 47.138 + LJRAND_T, 47.139 + LJRAND_N_PIECE_SETS 47.140 +}; 47.141 + 47.142 +enum { 47.143 + LJTS_OFF = 0, 47.144 + LJTS_TNT, 47.145 + LJTS_TDS, 47.146 + LJTS_TDS_NO_KICK, 47.147 + LJTS_N_ALGOS 47.148 +}; 47.149 + 47.150 +enum { 47.151 + LJSPD_ZERO = 0, 47.152 + LJSPD_RHYTHMZERO, 47.153 + LJSPD_EXP, 47.154 + LJSPD_RHYTHM, 47.155 + LJSPD_TGM, 47.156 + LJSPD_DEATH, 47.157 + LJSPD_DEATH300, 47.158 + LJSPD_NES, 47.159 + LJSPD_GB, 47.160 + LJSPD_GBHEART, 47.161 + LJSPD_N_CURVES 47.162 +}; 47.163 + 47.164 +enum { 47.165 + LJLOCK_NOW = 0, 47.166 + LJLOCK_SPAWN, 47.167 + LJLOCK_STEP, 47.168 + LJLOCK_MOVE, 47.169 + LJLOCK_N_STYLES 47.170 +}; 47.171 + 47.172 +enum { 47.173 + LJGRAV_NAIVE = 0, 47.174 + LJGRAV_STICKY, 47.175 + LJGRAV_STICKY_BY_COLOR, 47.176 + LJGRAV_CASCADE, 47.177 + LJGRAV_N_ALGOS 47.178 +}; 47.179 + 47.180 +enum { 47.181 + LJSCORE_LJ, 47.182 + LJSCORE_TNT64, 47.183 + LJSCORE_HOTLINE, 47.184 + LJSCORE_TDS, 47.185 + LJSCORE_NES, 47.186 + LJSCORE_LJ_NERF, 47.187 + LJSCORE_N_STYLES, 47.188 + LJSCORE_WORLDS, 47.189 + LJSCORE_TGM1, 47.190 + LJSCORE_IPOD 47.191 +}; 47.192 + 47.193 +enum { 47.194 + LJDROP_NOSCORE, 47.195 + LJDROP_NES, 47.196 + LJDROP_1CELL, 47.197 + LJDROP_1S_2H, 47.198 + LJDROP_N_STYLES 47.199 +}; 47.200 + 47.201 +enum { 47.202 + LJGARBAGE_NONE, 47.203 + LJGARBAGE_1, 47.204 + LJGARBAGE_2, 47.205 + LJGARBAGE_3, 47.206 + LJGARBAGE_4, 47.207 + LJGARBAGE_HRDERBY, 47.208 + LJGARBAGE_DRILL, 47.209 + LJGARBAGE_ZIGZAG, 47.210 + 47.211 + LJGARBAGE_N_STYLES 47.212 +}; 47.213 + 47.214 +enum { 47.215 + LJHOLD_NONE, 47.216 + LJHOLD_EMPTY, 47.217 + LJHOLD_TNT, 47.218 + LJHOLD_TO_NEXT, 47.219 + LJHOLD_N_STYLES 47.220 +}; 47.221 + 47.222 +enum { 47.223 + LJGLUING_NONE, 47.224 + LJGLUING_SQUARE, 47.225 + LJGLUING_STICKY, 47.226 + LJGLUING_STICKY_BY_COLOR, 47.227 + LJGLUING_N_STYLES 47.228 +}; 47.229 + 47.230 +// Guideline says 10 wide, but we want to support tetrinet mode 47.231 +#define LJ_PF_WID ((size_t)12) 47.232 +#define LJ_SPAWN_X ((LJ_PF_WID - 3) / 2) 47.233 +#define LJ_PF_HT ((size_t)24) 47.234 +#define LJ_PF_VIS_HT ((size_t)20) 47.235 +#define LJ_NEXT_PIECES 8 47.236 +#define LJ_MAX_LINES_PER_PIECE 8 47.237 + 47.238 +typedef struct LJBlkSpec 47.239 +{ 47.240 + signed char x, y, conn, reserved; 47.241 +} LJBlkSpec; 47.242 + 47.243 +typedef struct LJInput { 47.244 + signed char rotation, movement; 47.245 + unsigned char gravity; /* 5.3 format */ 47.246 + unsigned char other; 47.247 +} LJInput; 47.248 + 47.249 +typedef struct LJSpeedSetting { 47.250 + LJFixed gravity; 47.251 + unsigned char entryDelay, dasDelay, lockDelay, lineDelay; 47.252 +} LJSpeedSetting; 47.253 + 47.254 +typedef struct SpeedStateBase { 47.255 + unsigned char curve, section; 47.256 + unsigned char pad[2]; 47.257 + signed int level; 47.258 +} SpeedStateBase; 47.259 + 47.260 +#define MAX_BAG_LEN 10 47.261 +#define MAX_OMINO 4 47.262 + 47.263 +typedef struct LJField 47.264 +{ 47.265 + /* Game state */ 47.266 + LJBlock b[LJ_PF_HT][LJ_PF_WID]; 47.267 + LJBlock c[LJ_PF_HT][LJ_PF_WID]; 47.268 + LJBits clearedLines; 47.269 + LJBits sounds; 47.270 + LJBits tempRows; 47.271 + unsigned char curPiece[1 + LJ_NEXT_PIECES]; 47.272 + signed char permuPiece[2 * MAX_BAG_LEN], permuPhase; 47.273 + unsigned short nLineClears[LJ_MAX_LINES_PER_PIECE]; // [n-1] = # of n-line clears 47.274 + LJFixed y; 47.275 + LJState state; 47.276 + signed char stateTime; 47.277 + unsigned char theta; 47.278 + signed char x; 47.279 + signed char hardDropY; 47.280 + char alreadyHeld; 47.281 + char isSpin; 47.282 + char nLinesThisPiece; 47.283 + char canRotate; 47.284 + unsigned char upwardKicks; 47.285 + 47.286 + /* Persist from piece to piece */ 47.287 + int score, lines; 47.288 + unsigned int gameTime; // number of frames 47.289 + unsigned int activeTime; // number of frames 47.290 + signed short holdPiece; 47.291 + char chain; 47.292 + signed char garbage; 47.293 + unsigned char dropDist; 47.294 + unsigned char garbageX; 47.295 + unsigned short nPieces, outGarbage; 47.296 + unsigned short monosquares, multisquares; 47.297 + 47.298 + /* Determined by gimmick */ 47.299 + signed char gimmick; 47.300 + signed int bpmCounter; 47.301 + unsigned int speedupCounter; 47.302 + unsigned int goalCount; 47.303 + unsigned int seed; 47.304 + unsigned char goalType; 47.305 + 47.306 + LJSpeedSetting speed; 47.307 + SpeedStateBase speedState; 47.308 + 47.309 + unsigned char lockReset; // lockdown reset rule type 47.310 + unsigned char areStyle; 47.311 + unsigned char setLockDelay; // Overridden lock delay (255 = never) 47.312 + unsigned char setLineDelay; // Overridden line delay 47.313 + unsigned char garbageStyle; 47.314 + unsigned char ceiling; 47.315 + unsigned char enterAbove; 47.316 + unsigned char leftWall, rightWall; 47.317 + unsigned char pieceSet; 47.318 + unsigned char randomizer; 47.319 + unsigned char rotationSystem; 47.320 + unsigned char garbageRandomness; // 64: change garbageX in 1/4 of rows; 255: change all 47.321 + unsigned char tSpinAlgo; // 0: off, 1: TNT, 2: TDS 47.322 + unsigned char clearGravity; 47.323 + unsigned char gluing; // 0: off; 1: square 47.324 + unsigned char scoreStyle; 47.325 + unsigned char dropScoreStyle; 47.326 + unsigned char maxUpwardKicks; 47.327 + unsigned char holdStyle; 47.328 + unsigned char bottomBlocks; 47.329 + unsigned char reloaded; // 0: played through; 1: reloaded from saved state 47.330 +} LJField; 47.331 + 47.332 +/** 47.333 + * Names of the supported rotation systems (wktables.c). 47.334 + */ 47.335 +enum { 47.336 + LJROT_SRS, 47.337 + LJROT_SEGA, 47.338 + LJROT_ARIKA, 47.339 + LJROT_ATARI, 47.340 + LJROT_NES, 47.341 + LJROT_GB, 47.342 + LJROT_TOD, 47.343 + LJROT_TDX, 47.344 + N_ROTATION_SYSTEMS 47.345 +}; 47.346 + 47.347 +#define N_PIECE_SHAPES 10 47.348 +#define KICK_TABLE_LEN 5 47.349 +extern const char pieceColors[N_PIECE_SHAPES]; 47.350 +typedef unsigned char WallKickTable[4][KICK_TABLE_LEN]; 47.351 +typedef struct LJRotSystem { 47.352 + /** 47.353 + * Color scheme for this rotation system (0: SRS; 1: Sega) 47.354 + * Use colorset 0 (SRS) if all free-space kicks are WK(0, 0). 47.355 + * Otherwise, your rotation system has what Eddie Rogers has called 47.356 + * a topknot, and you should use Sega colors. 47.357 + */ 47.358 + unsigned char colorScheme; 47.359 + unsigned char reserved1[3]; 47.360 + unsigned char entryOffset[N_PIECE_SHAPES]; 47.361 + unsigned char entryTheta[N_PIECE_SHAPES]; 47.362 + /* These control which kick table is used for each piece. 47.363 + * If negative, no kick table is present. 47.364 + */ 47.365 + signed char kicksL[N_PIECE_SHAPES]; 47.366 + signed char kicksR[N_PIECE_SHAPES]; 47.367 + WallKickTable kickTables[]; 47.368 +} LJRotSystem; 47.369 + 47.370 +extern const LJRotSystem *const rotSystems[N_ROTATION_SYSTEMS]; 47.371 + 47.372 +#ifdef LJ_INTERNAL 47.373 + // Used for defining wall kick tables: 47.374 + // WK(x, y) builds a 1-byte record with 2 coordinates, at 4 bits 47.375 + // per coordinate. 47.376 + // WKX() and WKY() retrieve the X or Y coordinate from this record. 47.377 + // The ((stuff ^ 8) - 8) is sign extension magick 47.378 + #define WK(x, y) (((x) & 0x0F) | ((y) & 0x0F) << 4) 47.379 + #define WKX(wk) ((((wk) & 0x0F) ^ 8) - 8) 47.380 + #define WKY(wk) WKX((wk) >> 4) 47.381 + // If ARIKA_IF_NOT_CENTER is specified, check the free space 47.382 + // rotation for blocks in columns other than 1, and stop if 47.383 + // none of them are filled. This obsoletes the old SKIP_IF. 47.384 + #define SKIP_IF 0x80 47.385 + #define SKIP_IF3 0x82 47.386 + 47.387 + #define ARIKA_IF_NOT_CENTER 0x83 47.388 + #define WK_END 0x8F 47.389 +#if 0 47.390 + extern const WallKickTable *const wkTablesL[N_ROTATION_SYSTEMS][N_PIECE_SHAPES]; 47.391 + extern const WallKickTable *const wkTablesR[N_ROTATION_SYSTEMS][N_PIECE_SHAPES]; 47.392 +#endif 47.393 +#endif 47.394 + 47.395 +/** 47.396 + * Expands a tetromino to the blocks that make it up. 47.397 + * @param out an array of length 4 47.398 + * @param p the field into which the tetromino will be spawned 47.399 + * (used for default spawn orientation) 47.400 + * @param piece a piece number 47.401 + * @param xBase x coordinate of base position of the tetromino 47.402 + * @param yBase y coordinate of base position of the tetromino 47.403 + * @param theta the orientation of the tetromino 47.404 + * (0-3: use this; 4: use default spawn rotation) 47.405 + */ 47.406 +void expandPieceToBlocks(LJBlkSpec out[], 47.407 + const LJField *p, 47.408 + int piece, int xBase, int yBase, int theta); 47.409 + 47.410 +/** 47.411 + * Tests whether a particular block is occupied. 47.412 + * @param p the playfield 47.413 + * @param x the column 47.414 + * @param y the row (0 = bottom) 47.415 + * @return zero iff the space is open 47.416 + */ 47.417 +int isOccupied(const LJField *p, int x, int y); 47.418 + 47.419 +/** 47.420 + * Tests whether a particular tetromino would overlap one or more 47.421 + * blocks, a side wall, or the floor 47.422 + * @param p the playfield 47.423 + * @param x the column of the left side of the piece's bounding 4x4 47.424 + * @param y the row of the bottom of the piece's bounding 4x4 (0 = bottom) 47.425 + * @return zero iff the space is open 47.426 + */ 47.427 +int isCollision(const LJField *p, int x, int y, int theta); 47.428 + 47.429 +/** 47.430 + * Blanks a playfield and prepares it for a new game. 47.431 + * @param p the playfield 47.432 + */ 47.433 +void newGame(LJField *p); 47.434 + 47.435 +/** 47.436 + * Runs one frame of S.M.G. 47.437 + * @param p the playfield 47.438 + * @param in the player's input 47.439 + * @return the rows that were modified 47.440 + */ 47.441 +LJBits frame(LJField *p, const LJInput *in); 47.442 + 47.443 +/** 47.444 + * Applies gimmicks to a new game. 47.445 + * @param p the playfield 47.446 + * @param in the player's input 47.447 + */ 47.448 +void initGimmicks(LJField *p); 47.449 + 47.450 +/** 47.451 + * Runs gimmicks for one frame of S.M.G. 47.452 + * @param p the playfield 47.453 + * @param c the control settings 47.454 + * @return the rows that were modified 47.455 + */ 47.456 +struct LJControl; 47.457 +LJBits gimmicks(LJField *p, struct LJControl *c); 47.458 + 47.459 +/** 47.460 + * Sets up the randomizer. 47.461 + * @return the number of this piece 47.462 + */ 47.463 +void initRandomize(LJField *p); 47.464 + 47.465 +/** 47.466 + * Chooses a pseudo-random piece. 47.467 + * @param the field on which the piece will be generated 47.468 + * @return the number of this piece 47.469 + * (0=i, 1=j, 2=l, 3=o, 4=s, 5=t, 6=z) 47.470 + */ 47.471 +unsigned int randomize(LJField *p); 47.472 + 47.473 +/** 47.474 + * Counts the number of 1 bits in a bitfield. 47.475 + * @param p the bitfield 47.476 + * @return the number of bits in p with a value of 1. 47.477 + */ 47.478 +unsigned int countOnes(LJBits b); 47.479 + 47.480 +/** 47.481 + * Shuffles the columns of blocks in the playfield. 47.482 + * @param p the playfield 47.483 + */ 47.484 +void shuffleColumns(LJField *p); 47.485 + 47.486 +/** 47.487 + * Random number generator. Use this for all random events 47.488 + * that affect the game state. 47.489 + * @param field 47.490 + * @return uniformly distributed number in 0 to 0x7FFF 47.491 + */ 47.492 +unsigned int ljRand(LJField *p); 47.493 + 47.494 +/** 47.495 + * List of lines used for hotline scoring. 47.496 + * A line clear completed at row hotlineRows[i] is worth 100*(i + 1). 47.497 + */ 47.498 +extern const char hotlineRows[LJ_PF_HT]; 47.499 + 47.500 +/** 47.501 + * Counts the number of trailing zero bits on an integer. 47.502 + * For instance, 00000000 00000000 00000001 11111000 47.503 + * has 3 trailing zeroes. 47.504 + */ 47.505 +unsigned int bfffo(LJBits rowBits); 47.506 + 47.507 +void setupZigzagField(LJField *p, size_t height); 47.508 +unsigned int calcZigzagGrade(const LJField *p); 47.509 + 47.510 +#endif
48.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 48.2 +++ b/src/ljcontrol.h Fri Mar 13 00:39:12 2009 -0700 48.3 @@ -0,0 +1,109 @@ 48.4 +/* Control for LOCKJAW, an implementation of the Soviet Mind Game 48.5 + 48.6 +Copyright (C) 2006 Damian Yerrick <tepples+lj@spamcop.net> 48.7 + 48.8 +This work is free software; you can redistribute it and/or modify 48.9 +it under the terms of the GNU General Public License as published by 48.10 +the Free Software Foundation; either version 2 of the License, or 48.11 +(at your option) any later version. 48.12 + 48.13 +This program is distributed in the hope that it will be useful, 48.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 48.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 48.16 +GNU General Public License for more details. 48.17 + 48.18 +You should have received a copy of the GNU General Public License 48.19 +along with this program; if not, write to the Free Software 48.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 48.21 + 48.22 +Original game concept and design by Alexey Pajitnov. 48.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 48.24 +or The Tetris Company LLC. 48.25 + 48.26 +*/ 48.27 + 48.28 +#ifndef LJCONTROL_H 48.29 +#define LJCONTROL_H 48.30 + 48.31 +#include "ljtypes.h" 48.32 + 48.33 +// The cross-platform parts of the view are stored here 48.34 +typedef struct LJControl { 48.35 + LJBits lastKeys, repressKeys; 48.36 + unsigned int presses; 48.37 + unsigned char dasSpeed; 48.38 + unsigned char dasDelay; 48.39 + signed char dasCounter; 48.40 + unsigned char allowDiagonals; 48.41 + unsigned char softDropSpeed; 48.42 + unsigned char softDropLock; 48.43 + unsigned char hardDropLock; 48.44 + unsigned char initialDAS; 48.45 + unsigned char initialRotate; 48.46 + signed char countdown; 48.47 + struct LJReplay *replaySrc; 48.48 + struct LJReplay *replayDst; 48.49 +} LJControl; 48.50 + 48.51 +struct LJField; 48.52 +struct LJInput; 48.53 + 48.54 +#define N_SPEED_METER_PIECES 10 48.55 + 48.56 +typedef struct LJView { 48.57 + struct LJField *field; 48.58 + LJControl *control; 48.59 + LJBits backDirty; 48.60 + LJBits frontDirty; 48.61 + struct LJPCView *plat; 48.62 + unsigned char smoothGravity; 48.63 + unsigned char hideNext; 48.64 + unsigned char hideShadow; 48.65 + unsigned char nextPieces; 48.66 + unsigned char hidePF; 48.67 + unsigned char showTrails; 48.68 + unsigned char nLockTimes; 48.69 + unsigned int lockTime[N_SPEED_METER_PIECES]; 48.70 + LJFixed trailY; 48.71 +} LJView; 48.72 + 48.73 +enum { 48.74 + LJSHADOW_COLORED_25 = 0, 48.75 + LJSHADOW_COLORED_50, 48.76 + LJSHADOW_COLORED, 48.77 + LJSHADOW_COLORLESS, 48.78 + LJSHADOW_NONE, 48.79 + LJSHADOW_NO_FALLING, 48.80 + LJSHADOW_N_STYLES 48.81 +}; 48.82 + 48.83 +#define VKEY_UP 0x0001 48.84 +#define VKEY_DOWN 0x0002 48.85 +#define VKEY_LEFT 0x0004 48.86 +#define VKEY_RIGHT 0x0008 48.87 +#define VKEY_ROTL 0x0010 48.88 +#define VKEY_ROTR 0x0020 48.89 +#define VKEY_HOLD 0x0040 48.90 +#define VKEY_ITEM 0x0080 48.91 +#define VKEY_MACRO(x) (0x100 << (x)) 48.92 +#define VKEY_MACROS 0xFF00 48.93 +#define VKEY_START 0x00080000 48.94 + 48.95 +enum { 48.96 + LJZANGI_SLIDE, 48.97 + LJZANGI_LOCK, 48.98 + LJZANGI_LOCK_RELEASE, 48.99 + LJZANGI_N_STYLES 48.100 +}; 48.101 + 48.102 +/* VKEY_MACRO(0) to VKEY_MACRO(7) 48.103 + 48.104 +Event planners can restrict how many macros a player can use, 48.105 +so that keyboardists don't have an unfair advantage over 48.106 +gamepad users. 48.107 + 48.108 +*/ 48.109 + 48.110 +void addKeysToInput(struct LJInput *dst, LJBits keys, const struct LJField *p, LJControl *c); 48.111 + 48.112 +#endif
49.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 49.2 +++ b/src/ljds.c Fri Mar 13 00:39:12 2009 -0700 49.3 @@ -0,0 +1,334 @@ 49.4 +/* DS frontend for LOCKJAW, an implementation of the Soviet Mind Game 49.5 + 49.6 +Copyright (C) 2006-2007 Damian Yerrick <tepples+lj@spamcop.net> 49.7 + 49.8 +This work is free software; you can redistribute it and/or modify 49.9 +it under the terms of the GNU General Public License as published by 49.10 +the Free Software Foundation; either version 2 of the License, or 49.11 +(at your option) any later version. 49.12 + 49.13 +This program is distributed in the hope that it will be useful, 49.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 49.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 49.16 +GNU General Public License for more details. 49.17 + 49.18 +You should have received a copy of the GNU General Public License 49.19 +along with this program; if not, write to the Free Software 49.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 49.21 + 49.22 +Original game concept and design by Alexey Pajitnov. 49.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 49.24 +or The Tetris Company LLC. 49.25 + 49.26 +*/ 49.27 + 49.28 +#include "ljplay.h" 49.29 +#include "ljds.h" 49.30 +#include "talkback.h" 49.31 +#include <stdio.h> 49.32 +#include <string.h> 49.33 +#include "options.h" 49.34 +#include "gbamenus.h" 49.35 +#include "ljpath.h" 49.36 + 49.37 +#if 1 49.38 + #define LJ_VERSION "0.46 ("__DATE__")" 49.39 +#else 49.40 + #define LJ_VERSION "WIP ("__DATE__")" 49.41 +#endif 49.42 + 49.43 +#define SCREEN_W 32 49.44 +#define SCREEN_H 24 49.45 +#define DS_PFTOP 3 49.46 +#define DS_PFLEFT 10 49.47 + 49.48 +/* My ghetto IPC system */ 49.49 +volatile P8A7Talkback tb_cached = { 49.50 + .cmd = 0, 49.51 + .sounds = 0 49.52 +}; 49.53 + 49.54 +#define tb (*(volatile P8A7Talkback *) \ 49.55 + ((volatile char *)&tb_cached + 0x00400000)) 49.56 + 49.57 +short mouse_x, mouse_y; 49.58 +LJBits mouse_b; 49.59 + 49.60 +#include "ljgbads.inc" 49.61 + 49.62 +unsigned int nSprites = 0; 49.63 +volatile int curTime; 49.64 + 49.65 +void gba_play_sound(struct LJPCView *v, int n) { 49.66 + 49.67 +} 49.68 + 49.69 +void gba_poll_sound(struct LJPCView *plat) { 49.70 + 49.71 +} 49.72 + 49.73 +LJBits readHWKeys(void) { 49.74 + scanKeys(); 49.75 + LJBits j = keysHeld(); 49.76 + touchPosition xy = touchReadXY(); 49.77 + 49.78 + if (j & KEY_TOUCH) { 49.79 + mouse_x = xy.px; 49.80 + mouse_y = xy.py; 49.81 + mouse_b = 1; 49.82 + j &= ~(KEY_TOUCH_RIGHT | KEY_TOUCH_LEFT 49.83 + | KEY_TOUCH_UP | KEY_TOUCH_DOWN); 49.84 + if (xy.px < 96) { 49.85 + j |= KEY_TOUCH_LEFT; 49.86 + } else if (xy.px >= 160) { 49.87 + j |= KEY_TOUCH_RIGHT; 49.88 + } 49.89 + if (xy.py < 64) { 49.90 + j |= KEY_TOUCH_UP; 49.91 + } else if (xy.py >= 128) { 49.92 + j |= KEY_TOUCH_DOWN; 49.93 + } 49.94 + } else { 49.95 + mouse_b = 0; 49.96 + } 49.97 + return j; 49.98 +} 49.99 + 49.100 +void finishSprites(void) { 49.101 + for (int i = nSprites - 1; i >= 0; --i) { 49.102 + MAINOAM[i].attribute[0] = 512; 49.103 + } 49.104 + nSprites = 128; 49.105 +} 49.106 + 49.107 +void vsync(void) { 49.108 + swiWaitForVBlank(); 49.109 + wantPause |= needLidSleep(); 49.110 +} 49.111 + 49.112 +void isr(void) 49.113 +{ 49.114 + int interrupts = REG_IF; 49.115 + 49.116 + VBLANK_INTR_WAIT_FLAGS |= interrupts; 49.117 + REG_IF = interrupts; 49.118 + ++curTime; 49.119 +} 49.120 + 49.121 +#define KEY_X (IPC_X << 16) 49.122 +#define KEY_Y (IPC_Y << 16) 49.123 +#define KEY_PEN (IPC_PEN_DOWN << 16) 49.124 +#define VRAM_MAIN ((uint16 *)0x06000000) 49.125 +#define VRAM_SUB ((uint16 *)0x06200000) 49.126 + 49.127 + 49.128 + 49.129 +/** 49.130 + * Tells the ARM7 to play these sound effects. 49.131 + */ 49.132 +void playSoundEffects(LJView *v, LJBits sounds, int countdown) { 49.133 + tb.countdown = countdown; 49.134 + tb.sounds |= sounds; 49.135 +} 49.136 + 49.137 +#define SHADOW_BLOCK 0x00 49.138 + 49.139 +/** 49.140 + * Draws a tetromino whose lower left corner of the bounding box is at (x, y) 49.141 + * @param b the bitmap to draw to 49.142 + * @param piece the piece to be drawn 49.143 + * @param x distance from to left side of 4x4 box 49.144 + * @param y distance from top of bitmap to bottom of 4x4 box 49.145 + * @param the rotation state (0: U; 1: R; 2: D; 3: L; 4: Initial position) 49.146 + * @param w width of each block 49.147 + * @param h height of each block 49.148 + * @param color Drawing style 49.149 + * color == 0: draw shadow 49.150 + * color == 0x10 through 0x70: draw in that color 49.151 + * color == 0x80: draw as garbage 49.152 + * color == -255 through -1: draw with 255 through 1 percent lighting 49.153 + */ 49.154 +LJBits drawPiece(LJView *const v, void *const b, 49.155 + int piece, int x, int y, int theta, 49.156 + int color, int w, int h) { 49.157 + // Don't try to draw the -1 that's the sentinel for no hold piece 49.158 + if (piece < 0) 49.159 + return 0; 49.160 + 49.161 + LJBits rows = 0; 49.162 + LJBlkSpec blocks[4]; 49.163 + 49.164 + expandPieceToBlocks(blocks, v->field, piece, 0, 0, theta); 49.165 + 49.166 + for (int blk = 0; blk < 4; ++blk) { 49.167 + int blkValue = blocks[blk].conn; 49.168 + if (blkValue) { 49.169 + int blkX = blocks[blk].x; 49.170 + int blkY = blocks[blk].y; 49.171 + const int dstX = x + w * blkX; 49.172 + const int dstY = y + h * (-1 - blkY); 49.173 + 49.174 + if (color == 0x80) { 49.175 + blkValue = 0x8001; // garbage hold 49.176 + } else if (color != 0) { 49.177 + blkValue = ((blkValue & 0xF0) << 8) | 1; 49.178 + } else if (color == 0) { 49.179 + if (v->hideShadow == LJSHADOW_COLORED) { 49.180 + blkValue = ((blkValue & 0xF0) << 8) | 2; 49.181 + } else { 49.182 + blkValue = 0x8002; 49.183 + } 49.184 + } 49.185 + 49.186 + if (dstY > -8 && dstY < 192) { 49.187 + --nSprites; 49.188 + MAINOAM[nSprites].attribute[0] = dstY & 0x00FF; 49.189 + MAINOAM[nSprites].attribute[1] = dstX & 0x01FF; 49.190 + MAINOAM[nSprites].attribute[2] = blkValue; 49.191 + } 49.192 + 49.193 + rows |= 1 << blkY; 49.194 + } 49.195 + } 49.196 + 49.197 + return rows; 49.198 +} 49.199 + 49.200 +void openWindow(void) { 49.201 + videoSetMode(MODE_0_2D 49.202 + | DISPLAY_BG0_ACTIVE 49.203 + | DISPLAY_SPR_1D_LAYOUT 49.204 + | DISPLAY_SPR_ACTIVE); 49.205 + videoSetModeSub(MODE_0_2D 49.206 + | DISPLAY_BG0_ACTIVE); 49.207 + BGCTRL[0] = BG_16_COLOR | BG_TILE_BASE(0) | BG_MAP_BASE(31); 49.208 + BGCTRL_SUB[0] = BG_16_COLOR | BG_TILE_BASE(0) | BG_MAP_BASE(31); 49.209 + 49.210 + vramSetMainBanks(VRAM_A_MAIN_BG, VRAM_B_MAIN_SPRITE_0x06400000, 49.211 + VRAM_C_SUB_BG, VRAM_D_SUB_SPRITE); 49.212 + /* load_font(); */ 49.213 + // Load palette 49.214 + BG_PALETTE[0] = RGB5(31,31,31); 49.215 + BG_PALETTE[1] = RGB5( 0, 0,15); 49.216 + BG_PALETTE_SUB[0] = RGB5(0, 0, 0); 49.217 + setupPalette(srsColors); 49.218 + 49.219 + // Set scrolling 49.220 + BG_OFFSET[0].x = 0; 49.221 + BG_OFFSET[0].y = 0; 49.222 + BG_OFFSET_SUB[0].x = 0; 49.223 + BG_OFFSET_SUB[0].y = 0; 49.224 + 49.225 + SUB_BG2_XDX = 0x100; 49.226 + SUB_BG2_XDY = 0; 49.227 + SUB_BG2_YDX = 0; 49.228 + SUB_BG2_YDY = 0x100; 49.229 + SUB_BG2_CY = 0; 49.230 + SUB_BG2_CX = 0; 49.231 + 49.232 + lcdMainOnTop(); 49.233 +} 49.234 + 49.235 +void install_sound(void) { 49.236 + IPC->soundData = (void *)&tb; 49.237 +} 49.238 + 49.239 +#ifdef TRAP_SPRINTF 49.240 +int sprintf (char *dst, const char *format, ...) { 49.241 + BG_PALETTE[0] = RGB5(31, 0, 0); 49.242 + strcpy(dst, "[NO FPU]"); 49.243 + return 8; 49.244 +} 49.245 +#endif 49.246 + 49.247 +int main(void) { 49.248 + LJField p = { 49.249 + .leftWall = 1, 49.250 + .rightWall = 11, 49.251 + .ceiling = 20 49.252 + }; 49.253 + LJControl control = { 49.254 + .dasSpeed = 1, 49.255 + .dasDelay = 10, 49.256 + .initialDAS = 1, 49.257 + .allowDiagonals = 0, 49.258 + .softDropSpeed = 0, 49.259 + .softDropLock = 0, 49.260 + .hardDropLock = 1 49.261 + }; 49.262 + struct LJPCView platView; 49.263 + LJView mainView = { 49.264 + .field = &p, 49.265 + .control = &control, 49.266 + .smoothGravity = 1, 49.267 + .nextPieces = 3, 49.268 + .plat = &platView, 49.269 + .backDirty = ~0 49.270 + }; 49.271 + 49.272 + powerON(POWER_ALL_2D); 49.273 + initOptions(customPrefs); 49.274 + install_timer(); 49.275 + install_sound(); 49.276 + openWindow(); 49.277 + 49.278 + BG_PALETTE_SUB[0] = RGB5( 0, 0, 0); 49.279 + BG_PALETTE_SUB[1] = RGB5(10,20,10); 49.280 + BG_PALETTE_SUB[2] = RGB5(15,31,15); 49.281 + BG_PALETTE_SUB[3] = RGB5(15,31,15); 49.282 + vwfWinInit(&vwfTouch); 49.283 + { 49.284 + int x = vwfPuts(&vwfTouch, "finding memory card... ", 0, 0); 49.285 + if (ljpathInit("/data/lockjaw/lj.nds")) { 49.286 + vwfPuts(&vwfTouch, "success!", x, 0); 49.287 + } else { 49.288 + vwfPuts(&vwfTouch, "failed.", x, 0); 49.289 + vwfPuts(&vwfTouch, "To learn how to fix this, see", 0, 12); 49.290 + vwfPuts(&vwfTouch, "http://dldi.drunkencoders.com/", 0, 24); 49.291 + } 49.292 + } 49.293 + 49.294 + coprNotice(); 49.295 + 49.296 + load_font(); 49.297 + drawFrame(&mainView); 49.298 + 49.299 + while (1) { 49.300 + LJView *const players[1] = {&mainView}; 49.301 + 49.302 + for (int y = 0; y < LJ_PF_VIS_HT; ++y) { 49.303 + for (int x = 0; x < LJ_PF_WID; ++x) { 49.304 + p.b[y][x] = 0; 49.305 + } 49.306 + } 49.307 + updField(&mainView, ~0); 49.308 + 49.309 + // turn on sub display only long enough for options 49.310 + videoSetModeSub(MODE_0_2D 49.311 + | DISPLAY_BG0_ACTIVE); 49.312 + setupPalette(srsColors); 49.313 + options(&mainView, customPrefs); 49.314 + videoSetModeSub(MODE_0_2D); 49.315 + BGCTRL_SUB[0] = BG_16_COLOR | BG_TILE_BASE(0) | BG_MAP_BASE(31); 49.316 + BG_PALETTE_SUB[0] = RGB5(0, 0, 0); 49.317 + unpackCommonOptions(&mainView, customPrefs); 49.318 + 49.319 + p.seed = curTime ^ (curTime << 16); 49.320 + play(players, 1); 49.321 + 49.322 + tb.cmd = TALKBACK_STOP_MUSIC; 49.323 + BG_PALETTE[0] = (control.countdown > 0) 49.324 + ? RGB5(31, 15, 15) 49.325 + : RGB5(15, 31, 15); 49.326 + 49.327 + // play game over sound 49.328 + if (control.countdown > 0) { 49.329 + playSoundEffects(&mainView, 3 << 16, control.countdown); 49.330 + } 49.331 + for (int i = 0; i < 60; ++i) { 49.332 + vsync(); 49.333 + //gba_poll_sound(&platView); 49.334 + } 49.335 + debrief(&mainView); 49.336 + } 49.337 +}
50.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 50.2 +++ b/src/ljds.h Fri Mar 13 00:39:12 2009 -0700 50.3 @@ -0,0 +1,77 @@ 50.4 +/* DS frontend for LOCKJAW, an implementation of the Soviet Mind Game 50.5 + 50.6 +Copyright (C) 2006 Damian Yerrick <tepples+lj@spamcop.net> 50.7 + 50.8 +This work is free software; you can redistribute it and/or modify 50.9 +it under the terms of the GNU General Public License as published by 50.10 +the Free Software Foundation; either version 2 of the License, or 50.11 +(at your option) any later version. 50.12 + 50.13 +This program is distributed in the hope that it will be useful, 50.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 50.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 50.16 +GNU General Public License for more details. 50.17 + 50.18 +You should have received a copy of the GNU General Public License 50.19 +along with this program; if not, write to the Free Software 50.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 50.21 + 50.22 +Original game concept and design by Alexey Pajitnov. 50.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 50.24 +or The Tetris Company LLC. 50.25 + 50.26 +*/ 50.27 + 50.28 +#ifndef LJDS_H 50.29 +#define LJDS_H 50.30 + 50.31 +#include <nds.h> 50.32 +#include <stdlib.h> 50.33 +#include "ljcontrol.h" 50.34 + 50.35 +//#define SPR_VRAM(x) ((u32 *)(0x06420000 + 32 * x)) 50.36 +#define SPR_VRAM(x) ((u32 *)(0x06400000 + 32 * x)) 50.37 +#define MAINOAM ((SpriteEntry *)0x07000000) 50.38 + 50.39 +#define PATRAM4(x, tn) ((u32 *)(BG_TILE_RAM(0) | (((x) << 14) + ((tn) << 5)) )) 50.40 + 50.41 +#ifndef MAP 50.42 +typedef u16 NAMETABLE[32][32]; 50.43 + 50.44 +#define MAP ((NAMETABLE *)BG_MAP_RAM(0)) 50.45 +#define MAP_HFLIP 0x0400 50.46 +#define MAP_VFLIP 0x0800 50.47 +#define MAP_FLIP 0x0c00 50.48 +#define MAP_PALETTE(x) ((x) << 12) 50.49 +#endif 50.50 + 50.51 +struct LJPCView { 50.52 + const u16 *sndData[4]; 50.53 + u8 sndLeft[4]; 50.54 +}; 50.55 + 50.56 +#define KEY_TOUCH_RIGHT (KEY_RIGHT << 8) 50.57 +#define KEY_TOUCH_LEFT (KEY_LEFT << 8) 50.58 +#define KEY_TOUCH_UP (KEY_UP << 8) 50.59 +#define KEY_TOUCH_DOWN (KEY_DOWN << 8) 50.60 + 50.61 +extern short mouse_x, mouse_y; 50.62 +extern LJBits mouse_b; 50.63 + 50.64 +void textout(const char *str, int x, int y, int c); 50.65 +void isr(void); 50.66 +void cls(void); 50.67 +void vsync(void); 50.68 +LJBits readPad(unsigned int player); 50.69 +extern volatile int curTime; 50.70 + 50.71 +void install_sound(void); 50.72 +void gba_poll_sound(struct LJPCView *v); 50.73 +void gba_play_sound(struct LJPCView *v, int effect); 50.74 + 50.75 +extern unsigned char customPrefs[]; 50.76 +void options(LJView *view, unsigned char *prefs); 50.77 + 50.78 +int needLidSleep(void); 50.79 +void debrief(const LJView *v); 50.80 +#endif
51.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 51.2 +++ b/src/ljgba.c Fri Mar 13 00:39:12 2009 -0700 51.3 @@ -0,0 +1,230 @@ 51.4 +/* GBA frontend for LOCKJAW, an implementation of the Soviet Mind Game 51.5 + 51.6 +Copyright (C) 2006-2007 Damian Yerrick <tepples+lj@spamcop.net> 51.7 + 51.8 +This work is free software; you can redistribute it and/or modify 51.9 +it under the terms of the GNU General Public License as published by 51.10 +the Free Software Foundation; either version 2 of the License, or 51.11 +(at your option) any later version. 51.12 + 51.13 +This program is distributed in the hope that it will be useful, 51.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 51.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 51.16 +GNU General Public License for more details. 51.17 + 51.18 +You should have received a copy of the GNU General Public License 51.19 +along with this program; if not, write to the Free Software 51.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 51.21 + 51.22 +Original game concept and design by Alexey Pajitnov. 51.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 51.24 +or The Tetris Company LLC. 51.25 + 51.26 +*/ 51.27 + 51.28 +#include <stdio.h> 51.29 +#include <string.h> 51.30 +#include "ljgba.h" 51.31 +#include "ljplay.h" 51.32 +#include "options.h" 51.33 + 51.34 +#if 1 51.35 + #define LJ_VERSION "0.46 ("__DATE__")" 51.36 +#else 51.37 + #define LJ_VERSION "WIP ("__DATE__")" 51.38 +#endif 51.39 + 51.40 +#define SCREEN_W 30 51.41 +#define SCREEN_H 20 51.42 +#define DS_PFTOP 0 51.43 +#define DS_PFLEFT 9 51.44 +#include "ljgbads.inc" 51.45 + 51.46 +static unsigned int nSprites; 51.47 + 51.48 +LJBits readHWKeys(void) { 51.49 + return (~REG_KEYINPUT) & 0x03FF; 51.50 +} 51.51 + 51.52 +void finishSprites() { 51.53 + for (int i = nSprites - 1; i >= 0; --i) { 51.54 + OAM[i].attr0 = 512; 51.55 + } 51.56 + nSprites = 128; 51.57 +} 51.58 + 51.59 +void vsync(void) { 51.60 + VBlankIntrWait(); 51.61 +} 51.62 + 51.63 +void openWindow(void) { 51.64 + REG_DISPCNT = MODE_0 | BG0_ON | OBJ_ON; 51.65 + BGCTRL[0] = BG_TILE_BASE(0) | BG_MAP_BASE(31); 51.66 + BG_PALETTE[0] = RGB5(31, 31, 31); 51.67 + BG_PALETTE[1] = RGB5(0, 0, 0); 51.68 + SPRITE_PALETTE[0] = RGB5(31, 0, 31); 51.69 + SPRITE_PALETTE[1] = RGB5(0, 0, 0); 51.70 + setupPalette(srsColors); 51.71 +} 51.72 + 51.73 +void playSoundEffects(LJView *v, LJBits sounds, int countdown) { 51.74 + if (sounds & LJSND_IRS) { 51.75 + gba_play_sound(v->plat, 6); 51.76 + } else if (sounds & LJSND_ROTATE) { 51.77 + gba_play_sound(v->plat, 1); 51.78 + } 51.79 + if (sounds & LJSND_SHIFT) { 51.80 + gba_play_sound(v->plat, 0); 51.81 + } 51.82 + if (sounds & LJSND_LAND) { 51.83 + gba_play_sound(v->plat, 2); 51.84 + } 51.85 + if (sounds & LJSND_LOCK) { 51.86 + gba_play_sound(v->plat, 3); 51.87 + } 51.88 + if (sounds & LJSND_B2B) { 51.89 + gba_play_sound(v->plat, 8); 51.90 + } else if (sounds & LJSND_SETB2B) { 51.91 + gba_play_sound(v->plat, 7); 51.92 + } else if (sounds & LJSND_LINE) { 51.93 + gba_play_sound(v->plat, 4); 51.94 + } 51.95 + if (sounds & LJSND_HOLD) { 51.96 + gba_play_sound(v->plat, 5); 51.97 + } 51.98 + if (sounds & LJSND_SECTIONUP) { 51.99 + gba_play_sound(v->plat, 9); 51.100 + } 51.101 + gba_poll_sound(v->plat); 51.102 +} 51.103 + 51.104 +/** 51.105 + * Draws a tetromino whose lower left corner of the bounding box is at (x, y) 51.106 + * @param b the bitmap to draw to 51.107 + * @param piece the piece to be drawn 51.108 + * @param x distance from to left side of 4x4 box 51.109 + * @param y distance from top of bitmap to bottom of 4x4 box 51.110 + * @param the rotation state (0: U; 1: R; 2: D; 3: L; 4: Initial position) 51.111 + * @param w width of each block 51.112 + * @param h height of each block 51.113 + * @param color Drawing style 51.114 + * color == 0: draw shadow 51.115 + * color == 0x10 through 0x70: draw in that color 51.116 + * color == 0x80: draw as garbage 51.117 + * color == -255 through -1: draw with 255 through 1 percent lighting 51.118 + */ 51.119 +LJBits drawPiece(LJView *const v, void *const b, 51.120 + int piece, int x, int y, int theta, 51.121 + int color, int w, int h) { 51.122 + // Don't try to draw the -1 that's the sentinel for no hold piece 51.123 + if (piece < 0) 51.124 + return 0; 51.125 + 51.126 + LJBits rows = 0; 51.127 + LJBlkSpec blocks[4]; 51.128 + 51.129 + expandPieceToBlocks(blocks, v->field, piece, 0, 0, theta); 51.130 + 51.131 + for (int blk = 0; blk < 4; ++blk) { 51.132 + int blkValue = blocks[blk].conn; 51.133 + if (blkValue) { 51.134 + int blkX = blocks[blk].x; 51.135 + int blkY = blocks[blk].y; 51.136 + const int dstX = x + w * blkX; 51.137 + const int dstY = y + h * (-1 - blkY); 51.138 + 51.139 + if (color == 0x80) { 51.140 + blkValue = 0x8001; // garbage hold 51.141 + } else if (color != 0) { 51.142 + blkValue = ((blkValue & 0xF0) << 8) | 1; 51.143 + } else if (color == 0) { 51.144 + if (v->hideShadow == LJSHADOW_COLORED) { 51.145 + blkValue = ((blkValue & 0xF0) << 8) | 2; 51.146 + } else { 51.147 + blkValue = 0x0002; 51.148 + } 51.149 + } 51.150 + 51.151 + if (dstY > -8 && dstY < 160) { 51.152 + --nSprites; 51.153 + OAM[nSprites].attr0 = dstY & 0x00FF; 51.154 + OAM[nSprites].attr1 = dstX & 0x01FF; 51.155 + OAM[nSprites].attr2 = blkValue; 51.156 + } 51.157 + 51.158 + rows |= 1 << blkY; 51.159 + } 51.160 + } 51.161 + 51.162 + return rows; 51.163 +} 51.164 + 51.165 +extern unsigned char prefs[OPTIONS_MENU_LEN]; 51.166 + 51.167 +#include "gbamenus.h" 51.168 + 51.169 +int main(void) { 51.170 + LJField p = { 51.171 + .leftWall = 1, 51.172 + .rightWall = 11, 51.173 + .ceiling = 20 51.174 + }; 51.175 + LJControl control = { 51.176 + .dasSpeed = 1, 51.177 + .dasDelay = 10, 51.178 + .initialDAS = 1, 51.179 + .allowDiagonals = 0, 51.180 + .softDropSpeed = 0, 51.181 + .softDropLock = 0, 51.182 + .hardDropLock = 1 51.183 + }; 51.184 + struct LJPCView platView; 51.185 + LJView mainView = { 51.186 + .field = &p, 51.187 + .control = &control, 51.188 + .smoothGravity = 1, 51.189 + .plat = &platView, 51.190 + .backDirty = ~0 51.191 + }; 51.192 + 51.193 + videoSetMode(MODE_0_2D); 51.194 + BG_PALETTE[0] = RGB5(31, 0, 0); 51.195 + install_timer(); 51.196 + openWindow(); 51.197 + install_sound(&platView); 51.198 + initOptions(customPrefs); 51.199 + coprNotice(); 51.200 + 51.201 + while (1) { 51.202 + LJView *const players[1] = {&mainView}; 51.203 + REG_DISPCNT = MODE_0 | BG0_ON; 51.204 + BG_PALETTE[0] = RGB5(31, 31, 31); 51.205 + setupPalette(srsColors); 51.206 + options(&mainView, customPrefs); 51.207 + unpackCommonOptions(&mainView, customPrefs); 51.208 + 51.209 + p.seed = curTime ^ (curTime << 16); 51.210 + play(players, 1); 51.211 + BG_PALETTE[0] = (control.countdown > 0) 51.212 + ? RGB5(31, 15, 15) 51.213 + : RGB5(15, 31, 15); 51.214 + if (control.countdown > 0) { 51.215 + gba_play_sound(&platView, 10); 51.216 + gba_play_sound(&platView, 11); 51.217 + } else { 51.218 + gba_play_sound(&platView, 12); 51.219 + gba_play_sound(&platView, 13); 51.220 + } 51.221 + for (int i = 0; i < 60; ++i) { 51.222 + vsync(); 51.223 + gba_poll_sound(&platView); 51.224 + } 51.225 +#if 1 51.226 + debrief(&mainView); 51.227 +#else 51.228 + textout(" Press ", (SCREEN_W - 8) / 2, DS_PFTOP + 8, 0); 51.229 + textout(" Start ", (SCREEN_W - 8) / 2, DS_PFTOP + 9, 0); 51.230 + waitForStart(); 51.231 +#endif 51.232 + } 51.233 +}
52.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 52.2 +++ b/src/ljgba.h Fri Mar 13 00:39:12 2009 -0700 52.3 @@ -0,0 +1,51 @@ 52.4 +/* GBA frontend for LOCKJAW, an implementation of the Soviet Mind Game 52.5 + 52.6 +Copyright (C) 2006 Damian Yerrick <tepples+lj@spamcop.net> 52.7 + 52.8 +This work is free software; you can redistribute it and/or modify 52.9 +it under the terms of the GNU General Public License as published by 52.10 +the Free Software Foundation; either version 2 of the License, or 52.11 +(at your option) any later version. 52.12 + 52.13 +This program is distributed in the hope that it will be useful, 52.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 52.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 52.16 +GNU General Public License for more details. 52.17 + 52.18 +You should have received a copy of the GNU General Public License 52.19 +along with this program; if not, write to the Free Software 52.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 52.21 + 52.22 +Original game concept and design by Alexey Pajitnov. 52.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 52.24 +or The Tetris Company LLC. 52.25 + 52.26 +*/ 52.27 + 52.28 +#ifndef LJGBA_H 52.29 +#define LJGBA_H 52.30 + 52.31 +#include <gba.h> 52.32 +#include <stdlib.h> 52.33 +#include "ljcontrol.h" 52.34 + 52.35 +struct LJPCView { 52.36 + const u16 *sndData[4]; 52.37 + u8 sndLeft[4]; 52.38 +}; 52.39 + 52.40 +void textout(const char *str, int x, int y, int c); 52.41 +void isr(void); 52.42 +void cls(void); 52.43 +void vsync(void); 52.44 +LJBits readPad(unsigned int player); 52.45 +extern volatile int curTime; 52.46 + 52.47 +void install_sound(struct LJPCView *v); 52.48 +void gba_poll_sound(struct LJPCView *v); 52.49 +void gba_play_sound(struct LJPCView *v, int effect); 52.50 + 52.51 +extern unsigned char customPrefs[]; 52.52 +void options(LJView *view, unsigned char *prefs); 52.53 +void debrief(const LJView *v); 52.54 +#endif
53.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 53.2 +++ b/src/ljgbads.inc Fri Mar 13 00:39:12 2009 -0700 53.3 @@ -0,0 +1,579 @@ 53.4 +/* DS/GBA frontend for LOCKJAW, an implementation of the Soviet Mind Game 53.5 + 53.6 +Copyright (C) 2006-2007 Damian Yerrick <tepples+lj@spamcop.net> 53.7 + 53.8 +This work is free software; you can redistribute it and/or modify 53.9 +it under the terms of the GNU General Public License as published by 53.10 +the Free Software Foundation; either version 2 of the License, or 53.11 +(at your option) any later version. 53.12 + 53.13 +This program is distributed in the hope that it will be useful, 53.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 53.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 53.16 +GNU General Public License for more details. 53.17 + 53.18 +You should have received a copy of the GNU General Public License 53.19 +along with this program; if not, write to the Free Software 53.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 53.21 + 53.22 +Original game concept and design by Alexey Pajitnov. 53.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 53.24 +or The Tetris Company LLC. 53.25 + 53.26 +*/ 53.27 + 53.28 +#include "fontdraw.h" 53.29 + 53.30 +unsigned char wantPause; 53.31 +volatile char redrawWholeScreen = 0; 53.32 + 53.33 +void finishSprites(); 53.34 + 53.35 +void cls(void) { 53.36 + for (int y = 0; y < SCREEN_H; ++y) { 53.37 + for (int x = 0; x < SCREEN_W; ++x) { 53.38 + MAP[31][y][x] = ' '; 53.39 + } 53.40 + } 53.41 +} 53.42 + 53.43 +void drawFrame(LJView *v) { 53.44 + int left = v->field->leftWall; 53.45 + int right = v->field->rightWall; 53.46 + cls(); 53.47 + if (DS_PFTOP > 0) { 53.48 + for (int x = left - 1; x < right + 1; ++x) { 53.49 + MAP[31][DS_PFTOP - 1][DS_PFLEFT + x] = 0x8005; 53.50 + } 53.51 + } 53.52 + if (DS_PFTOP + LJ_PF_VIS_HT < SCREEN_H) { 53.53 + for (int x = left - 1; x < right + 1; ++x) { 53.54 + MAP[31][DS_PFTOP + LJ_PF_VIS_HT][DS_PFLEFT + x] = 0x8004; 53.55 + } 53.56 + } 53.57 + for (int i = 0; i < LJ_PF_VIS_HT; ++i) { 53.58 + MAP[31][i + DS_PFTOP][DS_PFLEFT + left - 1] = 0x8003; 53.59 + MAP[31][i + DS_PFTOP][DS_PFLEFT + right] = 0x8003; 53.60 + } 53.61 + textout("Score", 1, 6 + DS_PFTOP, 0); 53.62 + textout("Lines", 1, 8 + DS_PFTOP, 0); 53.63 + textout("Speed", 1, 10 + DS_PFTOP, 0); 53.64 +} 53.65 + 53.66 +static void drawBlock(struct LJPCView *unused, int x, int y, int b) { 53.67 + if (x >= 0 && x < LJ_PF_WID && y >= 0 && y < LJ_PF_VIS_HT) { 53.68 + int c; 53.69 + 53.70 + if (b == 0x100) { 53.71 + c = '-'; 53.72 + } else if (b == 0x110) { 53.73 + c = 0x8005; // ceiling tile 53.74 + } else if (b >= 0xA0 && b < 0xC0) { 53.75 + c = (b & 0xF0) << 8 | (b & 0x0F) | 0x100; 53.76 + } else if (b >= 0x10) { 53.77 + c = (b & 0xF0) << 8 | (b & 0x0F) | 0x10; 53.78 + } else { 53.79 + c = ' '; 53.80 + } 53.81 + MAP[31][DS_PFTOP + LJ_PF_VIS_HT - 1 - y][DS_PFLEFT + x] = c; 53.82 + } 53.83 +} 53.84 + 53.85 +void updField(const LJView *const v, LJBits rows) { 53.86 + const LJField *const p = v->field; 53.87 + 53.88 + for (int y = 0; 53.89 + y < LJ_PF_VIS_HT && rows != 0; 53.90 + ++y, rows >>= 1) { 53.91 + int blankTile = 0; 53.92 + 53.93 + if (y == p->ceiling) { 53.94 + blankTile = 0x110; 53.95 + } else if (hotlineRows[y] && v->field->scoreStyle == LJSCORE_HOTLINE) { 53.96 + blankTile = 0x100; 53.97 + } 53.98 + if (p->state == LJS_LINES_FALLING && p->stateTime > 0 53.99 + && ((1 << y) & p->tempRows)) { 53.100 + blankTile = 0x100; 53.101 + } 53.102 + if (rows & 1) { 53.103 + for (int x = p->leftWall; x < p->rightWall; x++) { 53.104 + int b = v->hidePF ? 0 : p->b[y][x]; 53.105 + drawBlock(v->plat, x, y, b ? b : blankTile); 53.106 + } 53.107 + } 53.108 + } 53.109 +} 53.110 + 53.111 +void blitField(LJView *v) { 53.112 + 53.113 +} 53.114 + 53.115 +int getTime() { 53.116 + return curTime; 53.117 +} 53.118 + 53.119 +#if !defined(DISP_VBLANK_IRQ) 53.120 +#define DISP_VBLANK_IRQ LCDC_VBL 53.121 +#endif 53.122 +#if !defined(IRQ_HANDLER) 53.123 +#define IRQ_HANDLER INT_VECTOR 53.124 +#endif 53.125 + 53.126 +void install_timer(void) 53.127 +{ 53.128 + 53.129 + // Turn off interrupts before doing anything 53.130 + REG_IME = 0; 53.131 + 53.132 + // Overwrite the ISR 53.133 + IRQ_HANDLER = isr; 53.134 + 53.135 + // Hook up the interrupt destination 53.136 + REG_IE = IRQ_VBLANK; 53.137 + 53.138 + // Acknowledge all pending interrupts 53.139 + REG_IF = ~0; 53.140 + 53.141 + // Set up an interrupt source 53.142 + REG_DISPSTAT = DISP_VBLANK_IRQ; 53.143 + 53.144 + // Turn interrupts back on 53.145 + REG_IME = 1; 53.146 +} 53.147 + 53.148 +void yieldCPU(void) { 53.149 + // we're not multitasking so we don't need this 53.150 + // on the GBA and DS, vsync() does all the waiting we need 53.151 +} 53.152 + 53.153 +static void upcvt_4bit(void *dst, const u8 *src, size_t len) 53.154 +{ 53.155 + u32 *out = dst; 53.156 + 53.157 + for(; len > 0; len--) 53.158 + { 53.159 + u32 dst_bits = 0; 53.160 + u32 src_bits = *src++; 53.161 + u32 x; 53.162 + 53.163 + for(x = 0; x < 8; x++) 53.164 + { 53.165 + dst_bits <<= 4; 53.166 + dst_bits |= src_bits & 1; 53.167 + src_bits >>= 1; 53.168 + } 53.169 + *out++ = dst_bits; 53.170 + } 53.171 +} 53.172 + 53.173 +extern const unsigned char text_chr[]; 53.174 +extern const unsigned int text_chr_size; 53.175 +extern const unsigned char gbablk_chr[]; 53.176 +extern const unsigned int gbablk_chr_size; 53.177 + 53.178 +static void loadOneConnection(void *in_dst, const void *in_src) { 53.179 + u16 *dst = in_dst; 53.180 + const u16 *src = in_src; 53.181 + for (unsigned int conn = 0; conn < 16; ++conn) { 53.182 + unsigned int topSegY = (conn & CONNECT_U) ? 32 : 0; 53.183 + unsigned int botSegY = (conn & CONNECT_D) ? 8 : 40; 53.184 + unsigned int leftSegX = (conn & CONNECT_L) ? 16 : 0; 53.185 + unsigned int rightSegX = (conn & CONNECT_R) ? 1 : 17; 53.186 + for (unsigned int i = 0; i < 8; i += 2) { 53.187 + *dst++ = src[leftSegX + topSegY + i]; 53.188 + *dst++ = src[rightSegX + topSegY + i]; 53.189 + } 53.190 + for (unsigned int i = 0; i < 8; i += 2) { 53.191 + *dst++ = src[leftSegX + botSegY + i]; 53.192 + *dst++ = src[rightSegX + botSegY + i]; 53.193 + } 53.194 + } 53.195 +} 53.196 + 53.197 +static void loadConnections(void) { 53.198 + loadOneConnection(PATRAM4(0, 16), gbablk_chr + 8*32); 53.199 + loadOneConnection(PATRAM4(0, 256), gbablk_chr + 12*32); 53.200 +} 53.201 + 53.202 +static void load_font(void) { 53.203 + upcvt_4bit(PATRAM4(0, 0), text_chr, text_chr_size); 53.204 + memcpy(PATRAM4(0, 0), gbablk_chr, 8*32); 53.205 + memcpy(SPR_VRAM(0), gbablk_chr, 8*32); 53.206 + loadConnections(); 53.207 +} 53.208 + 53.209 +void textout(const char *str, int x, int y, int c) { 53.210 + u16 *dst = &(MAP[31][y][x]); 53.211 + int spacesLeft = SCREEN_W - x; 53.212 + 53.213 + c <<= 12; 53.214 + while (*str != 0 && spacesLeft > 0) { 53.215 + *dst++ = c | *(unsigned char *)str++; 53.216 + --spacesLeft; 53.217 + } 53.218 +} 53.219 + 53.220 +static const u16 srsColors[12] = { 53.221 + RGB5(2, 2, 2), 53.222 + RGB5(0, 3, 3), 53.223 + RGB5(0, 0, 3), 53.224 + RGB5(3, 2, 0), 53.225 + RGB5(3, 3, 0), 53.226 + RGB5(0, 3, 0), 53.227 + RGB5(2, 0, 3), 53.228 + RGB5(3, 0, 0), 53.229 + RGB5(2, 2, 2), 53.230 + RGB5(3, 0, 0), 53.231 + RGB5(2, 2, 2), 53.232 + RGB5(3, 2, 1), 53.233 +}; 53.234 + 53.235 +static const u16 arsColors[12] = { 53.236 + RGB5(2, 2, 2), 53.237 + RGB5(3, 1, 0), 53.238 + RGB5(0, 0, 3), 53.239 + RGB5(3, 2, 0), 53.240 + RGB5(3, 3, 0), 53.241 + RGB5(2, 0, 3), 53.242 + RGB5(0, 3, 3), 53.243 + RGB5(0, 3, 0), 53.244 + RGB5(2, 2, 2), 53.245 + RGB5(3, 0, 0), 53.246 + RGB5(2, 2, 2), 53.247 + RGB5(3, 2, 1), 53.248 +}; 53.249 + 53.250 +void setupPalette(const u16 *colors) { 53.251 + for (int i = 1; i < 12; ++i) { 53.252 + int c = colors[i]; 53.253 + 53.254 + BG_PALETTE[i * 16 + 1] = RGB5(22,22,22) + 3 * c; 53.255 + BG_PALETTE[i * 16 + 2] = RGB5(13,13,13) + 6 * c; 53.256 + BG_PALETTE[i * 16 + 3] = RGB5( 4, 4, 4) + 9 * c; 53.257 + BG_PALETTE[i * 16 + 4] = RGB5( 4, 4, 4) + 7 * c; 53.258 + BG_PALETTE[i * 16 + 5] = RGB5( 4, 4, 4) + 5 * c; 53.259 + BG_PALETTE[i * 16 + 6] = RGB5( 4, 4, 4) + 3 * c; 53.260 + } 53.261 + memcpy(SPRITE_PALETTE, BG_PALETTE, 12 * 32); 53.262 +} 53.263 + 53.264 +// libnds style wrapper around libgba header 53.265 +#ifndef DISPLAY_BG0_ACTIVE 53.266 +#define DISPLAY_BG0_ACTIVE BG0_ON 53.267 +#define DISPLAY_SPR_ACTIVE OBJ_ON 53.268 +#define MODE_0_2D MODE_0 53.269 +#define DISPLAY_SPR_1D_LAYOUT OBJ_1D_MAP 53.270 +static inline void videoSetMode(int x) { 53.271 + REG_DISPCNT = x; 53.272 +} 53.273 + 53.274 +#endif 53.275 + 53.276 +void waitForStart(void) { 53.277 + LJBits lastJ = ~0; 53.278 + LJBits jnew = 0; 53.279 + 53.280 + do { 53.281 + LJBits j = ~REG_KEYINPUT; 53.282 + jnew = j & ~lastJ; 53.283 + lastJ = j; 53.284 + vsync(); 53.285 + } while(!(jnew & (KEY_A | KEY_START))); 53.286 +} 53.287 + 53.288 + 53.289 +static const char *const coprNoticeLines[] = { 53.290 + "LOCKJAW: The Reference", 53.291 + "Version "LJ_VERSION, 53.292 + NULL, 53.293 + "© 2008 Damian Yerrick", 53.294 + "Not sponsored or endorsed by Nintendo", 53.295 + "or Tetris Holding.", 53.296 + "Comes with ABSOLUTELY NO WARRANTY.", 53.297 + "This is free software, and you are welcome", 53.298 + "to share it under the conditions described", 53.299 + "in GPL.txt." 53.300 +}; 53.301 + 53.302 +void coprNotice(void) { 53.303 + videoSetMode(MODE_0_2D); 53.304 + BGCTRL[0] = BG_TILE_BASE(0) | BG_MAP_BASE(31); 53.305 + BG_OFFSET[0].x = 0; 53.306 + BG_OFFSET[0].y = 0; 53.307 + 53.308 + BG_PALETTE[0] = RGB5(31,31,31); 53.309 + BG_PALETTE[1] = RGB5(20,20,20); 53.310 + BG_PALETTE[2] = RGB5( 0, 0, 0); 53.311 + vwfWinInit(&vwfTop); 53.312 + for (int i = 0, y = 8; 53.313 + i < sizeof(coprNoticeLines) / sizeof(coprNoticeLines[0]); 53.314 + ++i) { 53.315 + if (coprNoticeLines[i]) { 53.316 + vwfPuts(&vwfTop, coprNoticeLines[i], 8, y); 53.317 + y += 12; 53.318 + } else { 53.319 + y += 6; 53.320 + } 53.321 + } 53.322 + vwfPuts(&vwfTop, "Press Start", 8, SCREEN_H * 8 - 16); 53.323 + videoSetMode(MODE_0_2D | DISPLAY_BG0_ACTIVE); 53.324 + waitForStart(); 53.325 +} 53.326 + 53.327 +LJBits menuReadPad(void) { 53.328 + LJBits keys = readPad(0); 53.329 + if (keys & VKEY_START) { 53.330 + keys |= VKEY_ROTR; 53.331 + } 53.332 + return keys; 53.333 +} 53.334 + 53.335 +void ljBeginDraw(LJView *v, int sync) { 53.336 + vsync(); 53.337 + finishSprites(); 53.338 +} 53.339 + 53.340 +void ljEndDraw(LJView *v) { 53.341 + 53.342 +} 53.343 + 53.344 +/* Replay stubs */ 53.345 +void replayRecord(struct LJReplay *r, LJBits keys, const LJInput *in) { 53.346 + 53.347 +} 53.348 + 53.349 +void replayClose(struct LJReplay *r) { 53.350 + 53.351 +} 53.352 + 53.353 +int getReplayFrame(struct LJReplay *r, LJInput *d) { 53.354 + return 0; 53.355 +} 53.356 + 53.357 +#define READY_GO_LINE 13 53.358 + 53.359 +void startingAnimation(LJView *v) { 53.360 + vsync(); 53.361 + gba_poll_sound(v->plat); 53.362 + setupPalette(rotSystems[v->field->rotationSystem]->colorScheme ? arsColors : srsColors); 53.363 + videoSetMode(MODE_0_2D); 53.364 + cls(); 53.365 + load_font(); 53.366 + BG_PALETTE[0] = RGB5(31,31,31); 53.367 + BG_PALETTE[1] = RGB5( 0, 0, 0); 53.368 + drawFrame(v); 53.369 + finishSprites(); 53.370 + vsync(); 53.371 + gba_poll_sound(v->plat); 53.372 + videoSetMode(MODE_0_2D 53.373 + | DISPLAY_BG0_ACTIVE); 53.374 + BGCTRL[0] = BG_TILE_BASE(0) | BG_MAP_BASE(31); 53.375 + BG_OFFSET[0].x = 0; 53.376 + BG_OFFSET[0].y = 0; 53.377 + 53.378 + textout("Ready", 53.379 + (LJ_PF_WID - 5) / 2 + DS_PFLEFT, 53.380 + DS_PFTOP + LJ_PF_VIS_HT - 1 - READY_GO_LINE, 53.381 + 0); 53.382 + for (int i = 0; i < 30; ++i) { 53.383 + vsync(); 53.384 + gba_poll_sound(v->plat); 53.385 + } 53.386 + v->backDirty = ~0; 53.387 + updField(v, ~0); 53.388 + videoSetMode(MODE_0_2D 53.389 + | DISPLAY_BG0_ACTIVE 53.390 + | DISPLAY_SPR_1D_LAYOUT 53.391 + | DISPLAY_SPR_ACTIVE); 53.392 + drawScore(v); 53.393 + finishSprites(); 53.394 + 53.395 + textout(" GO! ", 53.396 + (LJ_PF_WID - 5) / 2 + DS_PFLEFT, 53.397 + DS_PFTOP + LJ_PF_VIS_HT - 1 - READY_GO_LINE, 53.398 + 0); 53.399 + for (int i = 0; i < 30; ++i) { 53.400 + vsync(); 53.401 + gba_poll_sound(v->plat); 53.402 + } 53.403 + drawFrame(v); 53.404 + wantPause = 0; 53.405 + 53.406 +#ifdef ARM9 53.407 + tb.cmd = TALKBACK_PLAY_MUSIC; 53.408 +#endif 53.409 +} 53.410 + 53.411 +int pauseGame(struct LJPCView *v) { 53.412 + LJBits lastKeys = ~0; 53.413 + int unpaused = 0; 53.414 + int canceled = 0; 53.415 + 53.416 + // hide playfield 53.417 + for (int y = DS_PFTOP; y < DS_PFTOP + LJ_PF_VIS_HT; ++y) { 53.418 + for (int x = DS_PFLEFT; 53.419 + x < DS_PFLEFT + LJ_PF_WID; 53.420 + ++x) { 53.421 + MAP[31][y][x] = ' '; 53.422 + } 53.423 + } 53.424 + textout("Game", 2 + DS_PFLEFT, 6 + DS_PFTOP, 0); 53.425 + textout("Paused", 2 + DS_PFLEFT, 7 + DS_PFTOP, 0); 53.426 + textout("Start:", 2 + DS_PFLEFT, 10 + DS_PFTOP, 0); 53.427 + textout("Resume", 2 + DS_PFLEFT, 11 + DS_PFTOP, 0); 53.428 + textout("Select:", 2 + DS_PFLEFT, 13 + DS_PFTOP, 0); 53.429 + textout("Exit", 2 + DS_PFLEFT, 14 + DS_PFTOP, 0); 53.430 + 53.431 +#ifdef ARM9 53.432 + tb.cmd = TALKBACK_PAUSE_MUSIC; 53.433 +#endif 53.434 + while (!unpaused || (lastKeys & (KEY_SELECT | KEY_START))) { 53.435 + int keys = ~REG_KEYINPUT; 53.436 + if (keys & ~lastKeys & KEY_START) { 53.437 + unpaused = 1; 53.438 + } 53.439 + if (keys & ~lastKeys & KEY_SELECT) { 53.440 + unpaused = 1; 53.441 + canceled = 1; 53.442 + } 53.443 + finishSprites(); 53.444 + vsync(); 53.445 + gba_poll_sound(v); 53.446 + lastKeys = keys; 53.447 + } 53.448 +#ifdef ARM9 53.449 + tb.cmd = TALKBACK_PLAY_MUSIC; 53.450 +#endif 53.451 + return canceled; 53.452 +} 53.453 + 53.454 +int ljHandleConsoleButtons(LJView *v) { 53.455 + LJBits keys = ~REG_KEYINPUT; 53.456 + int canceled = 0; 53.457 + 53.458 + wantPause |= !!(keys & KEY_START); 53.459 + if (wantPause) { 53.460 + canceled = pauseGame(v->plat); 53.461 + wantPause = 0; 53.462 + drawFrame(v); 53.463 + v->backDirty = ~0; 53.464 + } 53.465 + return canceled; 53.466 +} 53.467 + 53.468 +LJBits drawPiece(LJView *const v, void *const b, 53.469 + int piece, int x, int y, int theta, 53.470 + int color, int w, int h); 53.471 + 53.472 +void drawFallingPiece(LJView *v) { 53.473 + LJBits bits = 0; 53.474 + const LJField *const p = v->field; 53.475 + int piece = p->curPiece[0]; 53.476 + int y = ljfixfloor(p->y); 53.477 + const int w = 8; 53.478 + const int h = 8; 53.479 + int drawnY = v->smoothGravity ? ljfixfloor(h * p->y) : h * y; 53.480 + const int color = (p->state == LJS_LANDED) 53.481 + ? -128 - ((p->stateTime + 1) * 128 / (p->speed.lockDelay + 1)) 53.482 + : pieceColors[piece]; 53.483 + 53.484 + bits = drawPiece(v, NULL, piece, 53.485 + w * (p->x + DS_PFLEFT), 53.486 + h * (LJ_PF_VIS_HT + DS_PFTOP) - drawnY, 53.487 + p->theta, 53.488 + color, w, h); 53.489 + bits = (y >= 0) ? bits << y : bits >> -y; 53.490 + bits &= (1 << LJ_PF_VIS_HT) - 1; 53.491 + 53.492 + v->backDirty |= bits | (bits << 1); 53.493 + v->frontDirty |= bits | (bits << 1); 53.494 +} 53.495 + 53.496 +#define SHADOW_BLOCK 0x00 53.497 + 53.498 +void drawShadow(LJView *v) { 53.499 + LJBits bits = 0; 53.500 + const LJField *const p = v->field; 53.501 + int piece = p->curPiece[0]; 53.502 + int y = p->hardDropY; 53.503 + const int w = 8; 53.504 + const int h = 8; 53.505 + 53.506 + bits = drawPiece(v, NULL, piece, 53.507 + w * (p->x + DS_PFLEFT), 53.508 + h * (LJ_PF_VIS_HT + DS_PFTOP - y), 53.509 + p->theta, 53.510 + SHADOW_BLOCK, w, h); 53.511 + bits = (y >= 0) ? bits << y : bits >> -y; 53.512 + bits &= (1 << LJ_PF_VIS_HT) - 1; 53.513 + 53.514 + v->backDirty |= bits; 53.515 + v->frontDirty |= bits; 53.516 +} 53.517 + 53.518 +void drawNextPieces(LJView *v) { 53.519 + int holdPieceColor = v->field->alreadyHeld 53.520 + ? 0x80 53.521 + : pieceColors[v->field->holdPiece]; 53.522 + 53.523 + // Draw hold piece 53.524 + drawPiece(v, NULL, 53.525 + v->field->holdPiece, 53.526 + (DS_PFLEFT - 5) * 8, (DS_PFTOP + 5) * 8, 4, 53.527 + holdPieceColor, 8, 8); 53.528 + 53.529 + // Draw next pieces 53.530 + int y = 32 + 8 * DS_PFTOP; 53.531 + int x = (DS_PFLEFT + LJ_PF_WID) * 8; 53.532 + for(int i = 1; i <= v->nextPieces; ++i) { 53.533 + int piece = v->field->curPiece[i]; 53.534 + 53.535 + if (!v->hideNext) { 53.536 + drawPiece(v, NULL, 53.537 + piece, x, y, 4, 53.538 + pieceColors[piece], 8, 8); 53.539 + } 53.540 + y += 20; 53.541 + } 53.542 + v->frontDirty &= (1 << LJ_PF_VIS_HT) - 1; 53.543 +} 53.544 + 53.545 +void drawScore(LJView *v) { 53.546 + char txt[16]; 53.547 + int tpm = -1; 53.548 + int lvl = v->field->speedState.level; 53.549 + 53.550 + siprintf(txt, "%8u", v->field->score); 53.551 + textout(txt, 0, 7 + DS_PFTOP, 0); 53.552 + siprintf(txt, "%8u", v->field->lines); 53.553 + textout(txt, 0, 9 + DS_PFTOP, 0); 53.554 + 53.555 + if (lvl > 0) { 53.556 + textout("Level:", 1, SCREEN_H - 3, 0); 53.557 + siprintf(txt, "%9u", lvl); 53.558 + textout(txt, 0, SCREEN_H - 2, 0); 53.559 + } 53.560 + 53.561 + if (v->nLockTimes >= 2) { 53.562 + int time = v->lockTime[0] - v->lockTime[v->nLockTimes - 1]; 53.563 + if (time > 0) { 53.564 + tpm = 3600 * (v->nLockTimes - 1) / time; 53.565 + } 53.566 + } 53.567 + if (tpm > 0) { 53.568 + siprintf(txt, "%8d", tpm); 53.569 + textout(txt, 0, 11 + DS_PFTOP, 0); 53.570 + } else { 53.571 + textout(" ---", 0, 11 + DS_PFTOP, 0); 53.572 + } 53.573 + 53.574 + { 53.575 + int seconds = v->field->gameTime / 60; 53.576 + int minutes = seconds / 60; 53.577 + seconds -= minutes * 60; 53.578 + siprintf(txt, "%6d:%02d", minutes, seconds); 53.579 + textout(txt, 0, SCREEN_H - 1, 0); 53.580 + } 53.581 + drawNextPieces(v); 53.582 +}
54.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 54.2 +++ b/src/ljlocale.c Fri Mar 13 00:39:12 2009 -0700 54.3 @@ -0,0 +1,299 @@ 54.4 +#include "ljlocale.h" 54.5 +#include <stdlib.h> 54.6 +#include <string.h> 54.7 + 54.8 + 54.9 +static const FourCCLocaleEntry *curLocale = englishLocale; 54.10 + 54.11 +void ljSetLocale(const FourCCLocaleEntry *locale) { 54.12 + if (locale) { 54.13 + curLocale = locale; 54.14 + } 54.15 +} 54.16 + 54.17 +static const FourCCLocaleEntry *ljGetFourCCEntry(unsigned int i) { 54.18 + const FourCCLocaleEntry *base = curLocale; 54.19 + 54.20 + for(; base->code.i != 0; ++base) { 54.21 + if (base->code.i == i) { 54.22 + return base; 54.23 + } 54.24 + } 54.25 + return NULL; 54.26 +} 54.27 + 54.28 +const char *ljGetFourCCName(FourCC i) { 54.29 + const FourCCLocaleEntry *ent = ljGetFourCCEntry(i.i); 54.30 + return ent ? ent->name : NULL; 54.31 +} 54.32 + 54.33 +const char *ljGetFourCCDesc(FourCC i) { 54.34 + const FourCCLocaleEntry *ent = ljGetFourCCEntry(i.i); 54.35 + return ent ? ent->desc : NULL; 54.36 +} 54.37 + 54.38 +const FourCCLocaleEntry englishLocale[] = { 54.39 + /* Page names */ 54.40 + /* omitted */ 54.41 + 54.42 + /* Option names */ 54.43 + {{"gimm"}, "Gimmick", 54.44 + "Goal or other game mode" }, 54.45 + {{"pfw"}, "Well width", 54.46 + "Amount of space to build in" }, 54.47 + {{"pfh"}, "Well height", 54.48 + "Amount of space to build in" }, 54.49 + {{"vzsp"}, "Enter above ceiling", 54.50 + "Pieces start below or above top of well" }, 54.51 + {{"spdc"}, "Speed curve", 54.52 + "Controls speed of falling piece and delays" }, 54.53 + {{"are"}, "Max entry delay", 54.54 + "Time from lockdown to next piece entry" }, 54.55 + {{"piec"}, "Piece set", 54.56 + "Which pieces will be used" }, 54.57 + {{"rand"}, "Randomizer", 54.58 + "How the pieces are shuffled" }, 54.59 + {{"hold"}, "Hold piece", 54.60 + "Press Hold to save a piece for later" }, 54.61 + {{"rots"}, "Rotation system", 54.62 + "How pieces turn and react to walls" }, 54.63 + {{"upkl"}, "Floor kicks", 54.64 + "Times each piece can rotate upward" }, 54.65 + {{"srk"}, "Sub-row kicks", 54.66 + "" }, 54.67 + {{"lock"}, "Lockdown", 54.68 + "Start of time when a piece slides around" }, 54.69 + {{"sldt"}, "Lock delay", 54.70 + "Amount of time a piece can slide around" }, 54.71 + {{"deep"}, "Deep drop", 54.72 + "Hard drop past blocks in the well" }, 54.73 + {{"clrd"}, "Line clear delay", 54.74 + "Time for cleared lines to disappear" }, 54.75 + {{"grav"}, "Clear gravity", 54.76 + "How blocks above a cleared line fall" }, 54.77 + {{"glue"}, "Gluing", 54.78 + "How blocks stick together" }, 54.79 + {{"lsco"}, "Line scoring", 54.80 + "Points awarded for lines" }, 54.81 + {{"dsco"}, "Drop scoring", 54.82 + "Points awarded for fast play (Up/Down)" }, 54.83 + {{"tspn"}, "T-spin detection", 54.84 + "Detect turning a piece into a tight space" }, 54.85 + {{"garb"}, "Garbage", 54.86 + "Blocks added to the bottom of the well" }, 54.87 + {{"dasd"}, "Max sideways delay", 54.88 + "Hold left/right this long to move fast" }, 54.89 + {{"dass"}, "Sideways speed", 54.90 + "How fast piece moves left/right" }, 54.91 + {{"idas"}, "Initial sideways motion", 54.92 + "Allow shifting as a piece is coming out?" }, 54.93 + {{"irs"}, "Initial rotation", 54.94 + "Hold rotate to turn each piece as it enters" }, 54.95 + {{"8way"}, "Allow diagonal motion", 54.96 + "Turn off to prevent accidental hard drops" }, 54.97 + {{"sfds"}, "Soft drop speed", 54.98 + "Hold down to drop the piece this fast" }, 54.99 + {{"sfdl"}, "Soft drop", 54.100 + "Behavior when a piece lands with down" }, 54.101 + {{"hrdl"}, "Hard drop", 54.102 + "Behavior when a piece lands with up" }, 54.103 + {{"tls"}, "Shadow", 54.104 + "Shows where the piece will land" }, 54.105 + {{"invs"}, "Hide blocks in well", 54.106 + "Invisible challenge tests your memory" }, 54.107 + {{"next"}, "Next pieces", 54.108 + "Number of previewed pieces" }, 54.109 + {{"srph"}, "Smooth gravity", 54.110 + "Turn off to make the piece fall row-by-row" }, 54.111 + {{"inpv"}, "Next above shadow", 54.112 + "Number of previewed pieces inside the well" }, 54.113 + {{"mblr"}, "Drop trails", 54.114 + "Makes hard drops look harder" }, 54.115 + 54.116 + {{"lidp"}, "Pause on task switch", 54.117 + "Makes Alt+Tab or screensaver safe" }, 54.118 + {{"rec"}, "Record all games", 54.119 + "Save each game's replay to demo.ljm" }, 54.120 + {{"wndw"}, "Display mode", 54.121 + "Shut out distractions" }, 54.122 + 54.123 + /* Booleans */ 54.124 + {{"Off"}, "Off" }, 54.125 + {{"On"}, "On" }, 54.126 + 54.127 + /* Gimmicks */ 54.128 + {{"Mara"}, "Marathon", 54.129 + "Play until you top out."}, 54.130 + {{"Line"}, "40 lines", 54.131 + "Game ends after you clear this many lines."}, 54.132 + {{"Time"}, "180 seconds", 54.133 + "Game ends after this much time."}, 54.134 + {{"DrlA"}, "Drill Attack", 54.135 + "Clear the bottom row of garbage to win."}, 54.136 + {{"Item"}, "Vs. w/Items", 54.137 + "Three opponents send disabling attacks."}, 54.138 + {{"Keys"}, "Baboo!", 54.139 + "What can you do with 300 keystrokes?"}, 54.140 + 54.141 + /* Garbage */ 54.142 + {{"HRD"}, "Home Run Derby", 54.143 + "Clear 1, 2, or 3 lines and get garbage back"}, 54.144 + {{"DrlG"}, "Drill", 54.145 + "Well is filled with random garbage"}, 54.146 + {{"Zigz"}, "Zigzag", 54.147 + "Well is filled with zigzag garbage"}, 54.148 + 54.149 + /* Piece set */ 54.150 + {{"AllP"}, "All pieces", 54.151 + "Pieces made of two, three, or four blocks" }, 54.152 + {{"IJLO"}, "Tetrominoes", 54.153 + "Pieces made of four blocks"}, 54.154 + {{"JLOT"}, "Tetrominoes no I", 54.155 + "Four blocks, no straight pieces"}, 54.156 + {{"SZSZ"}, "S and Z only" }, 54.157 + {{"IIII"}, "iCheat(tm)", 54.158 + "All I (straight) tetrominoes" }, 54.159 + {{"TTTT"}, "T-Party", 54.160 + "All T tetrominoes" }, 54.161 + 54.162 + /* Randomizer */ 54.163 + {{"Unif"}, "Memoryless", 54.164 + "Equal probability to deal any piece" }, 54.165 + {{"Bag"}, "Bag", 54.166 + "Deals one of each piece before repeats"}, 54.167 + {{"Bag+"}, "Bag + 1", 54.168 + "Adds one extra to each set of pieces"}, 54.169 + {{"Bag2"}, "Double bag", 54.170 + "Deals two of each piece before repeats"}, 54.171 + {{"Hist"}, "Strict history", 54.172 + "Does not deal a recent piece"}, 54.173 + {{"His6"}, "History 6 rolls", 54.174 + "Rarely deals a recent piece"}, 54.175 + 54.176 + /* Speed curve */ 54.177 + {{"Exp"}, "Exponential", 54.178 + "Game gradually gets faster"}, 54.179 + {{"Rh20"}, "Rhythm 20G", 54.180 + "Slide pieces into place to the beat"}, 54.181 + {{"Rhy0"}, "Rhythm 0G", 54.182 + "Drop pieces into place to the beat"}, 54.183 + {{"TGM2"}, "Master" }, 54.184 + {{"TAPD"}, "Death" }, 54.185 + {{"TAD3"}, "Death 300+" }, 54.186 + {{"NESc"}, "NES" }, 54.187 + {{"GBc"}, "Game Boy" }, 54.188 + {{"GBHL"}, "Game Boy Heart" }, 54.189 + 54.190 + /* Hold */ 54.191 + {{"HldE"}, "On, empty", 54.192 + "First held piece brings out next"}, 54.193 + {{"HldR"}, "On, random", 54.194 + "Random piece in hold box at start"}, 54.195 + {{"HldN"}, "Hold to next", 54.196 + "Hold swaps falling and next (like Radica)"}, 54.197 + 54.198 + /* Rotation systems */ 54.199 + {{"SRS"}, "SRS", 54.200 + "LOL T-spin triples"}, 54.201 + {{"Sega"}, "Sega 1988" }, 54.202 + {{"ARS"}, "Arika", 54.203 + "The rotation system of grandmasters"}, 54.204 + {{"Tngn"}, "Tengen", 54.205 + "Unlicensed" }, 54.206 + {{"NRSL"}, "Game Boy", 54.207 + "Left-handed system"}, 54.208 + {{"NRSR"}, "NES", 54.209 + "Right-handed system"}, 54.210 + {{"TOD4"}, "TOD M4", 54.211 + "Like SRS but less radical" }, 54.212 + {{"TDX"}, "Climbing", 54.213 + "Spider-Man's favorite Tetris game is DX"}, 54.214 + 54.215 + /* Lockdown */ 54.216 + {{"OLD"}, "Classic", 54.217 + "Lock when piece lands" }, 54.218 + {{"EntR"}, "Entry reset", 54.219 + "Lock delay resets for each new piece" }, 54.220 + {{"StpR"}, "Step reset", 54.221 + "Lock delay resets when piece moves down" }, 54.222 + {{"MovR"}, "Move reset", 54.223 + "Lock delay resets when piece moves" }, 54.224 + 54.225 + /* Lock/line delay and floor kicks */ 54.226 + {{"SBSC"}, "Set by speed curve" }, 54.227 + {{"Inf"}, "Unlimited" }, 54.228 + 54.229 + /* Sub-row kick */ 54.230 + {{"srkN"}, "Unchanged" }, 54.231 + {{"srkM"}, "Kick minimal distance" }, 54.232 + {{"srkU"}, "Always kick up" }, 54.233 + 54.234 + /* Gluing and gravity */ 54.235 + {{"Naiv"}, "Naive", 54.236 + "Can result in floating blocks"}, 54.237 + {{"Squ"}, "Square", 54.238 + "Form a 4x4 square from complete pieces" }, 54.239 + {{"Stky"}, "Sticky", 54.240 + "Blocks stick together"}, 54.241 + {{"byCo"}, "Sticky by color", 54.242 + "Blocks of each color stick together"}, 54.243 + {{"Casc"}, "Cascade", 54.244 + "Each piece falls separately" }, 54.245 + 54.246 + /* Line scoring */ 54.247 + {{"LJ"}, "LJ", 54.248 + "100, 400, 700, 1200, B2B=200, T=500*n" }, 54.249 + {{"Fibo"}, "Fibonacci", 54.250 + "100, 200, 300, 500 (800, 1300, ...)" }, 54.251 + {{"HotL"}, "Hotline", 54.252 + "100-600 for clearing lines on specific rows" }, 54.253 + {{"TDSl"}, "Guideline", 54.254 + "Level * 100, 300, 500, 800, B2B=50%, T=?" }, 54.255 + {{"NESl"}, "8-bit", 54.256 + "Level * 40, 100, 300, 1200" }, 54.257 + {{"LJns"}, "LJ (nerfed spin)", 54.258 + "100, 400, 700, 1200, B2B=200, T=300*n" }, 54.259 + 54.260 + /* Drop scoring */ 54.261 + {{"ConD"}, "Continuous drop", 54.262 + "1 point per row dropped without stopping"}, 54.263 + {{"S1H1"}, "Drop", 54.264 + "1 point per row soft or hard dropped"}, 54.265 + {{"S1H2"}, "Soft x1, Hard x2", 54.266 + "1 pt/row for soft drop, 2 for hard drop"}, 54.267 + 54.268 + /* T-spin */ 54.269 + {{"Imob"}, "Immobile", 54.270 + "Rotate any piece so it can't move up"}, 54.271 + {{"CrnT"}, "3-corner T", 54.272 + "Rotate T with 3 corners filled" }, 54.273 + {{"WKT"}, "3-corner T no kick", 54.274 + "Rotate T with 3 corners filled, without kick" }, 54.275 + 54.276 + /* sideways speed, soft drop speed, zangi */ 54.277 + 54.278 + /* Zangi */ 54.279 + {{"Slid"}, "Slide", 54.280 + "The piece can be dropped and then moved"}, 54.281 + {{"Lock"}, "Lock", 54.282 + "The piece locks immediately"}, 54.283 + {{"LocU"}, "Lock on release", 54.284 + "Can be moved while drop key is pressed"}, 54.285 + 54.286 + /* Shadow */ 54.287 + {{"tl25"}, "Fainter color" }, 54.288 + {{"tl50"}, "Faint color" }, 54.289 + {{"tlsC"}, "Color" }, 54.290 + {{"tlsM"}, "Monochrome" }, 54.291 + {{"invF"}, "Off, hide falling piece too"}, 54.292 + 54.293 +#ifdef HAS_FPU 54.294 + {{"FulS"}, "Full screen", 54.295 + "Try to run the game in the full screen."}, 54.296 + {{"Wind"}, "Windowed", 54.297 + "Run the game in a window."}, 54.298 +#endif 54.299 + 54.300 + /* list terminator */ 54.301 + { {.i=0}, NULL } 54.302 +};
55.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 55.2 +++ b/src/ljlocale.h Fri Mar 13 00:39:12 2009 -0700 55.3 @@ -0,0 +1,33 @@ 55.4 +#ifndef LJLOCALE_H 55.5 +#define LJLOCALE_H 55.6 +#include <stdint.h> 55.7 + 55.8 +/* 55.9 +A FourCC, or four-character code, is a short string that fits in a 55.10 +32-bit word. 55.11 +*/ 55.12 +typedef union FourCC { 55.13 + char c[4]; 55.14 + uint32_t i; 55.15 +} FourCC; 55.16 + 55.17 +/* When abusing union, test that the types that the compiler uses are 55.18 + the same types the developer's compiler uses. */ 55.19 +extern char sizeof_uint32_t_equals_4[(sizeof(uint32_t) == 4) ? 1 : -1]; 55.20 + 55.21 +typedef struct FourCCLocaleEntry { 55.22 + FourCC code; 55.23 + const char *name; 55.24 + const char *desc; 55.25 +} FourCCLocaleEntry; 55.26 + 55.27 +void ljSetLocale(const FourCCLocaleEntry *locale); 55.28 + 55.29 +#define _4CC(i) (ljGetFourCCName((FourCC){i})) 55.30 +const char *ljGetFourCCName(FourCC i); 55.31 + 55.32 +#define _4CCDESC(i) (ljGetFourCCDesc((FourCC){i})) 55.33 +const char *ljGetFourCCDesc(FourCC i); 55.34 + 55.35 +extern const FourCCLocaleEntry englishLocale[]; 55.36 +#endif
56.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 56.2 +++ b/src/ljmusic.c Fri Mar 13 00:39:12 2009 -0700 56.3 @@ -0,0 +1,204 @@ 56.4 +/* FIXME: add license block */ 56.5 +#include <allegro.h> 56.6 +#include "ljmusic.h" 56.7 + 56.8 +#if LJMUSIC_USING_DUMB 56.9 +#include <aldumb.h> 56.10 +static int dumb_inited; 56.11 +#endif 56.12 +#if LJMUSIC_USING_VORBIS 56.13 +#include "ljvorbis.h" 56.14 +#endif 56.15 + 56.16 +#define WARNINGS 0 56.17 + 56.18 +struct LJMusic { 56.19 + int paused; 56.20 +#if LJMUSIC_USING_VORBIS 56.21 + LJVorbis *ogg; 56.22 +#endif 56.23 +#if LJMUSIC_USING_DUMB 56.24 + DUH *duh; 56.25 + AL_DUH_PLAYER *duhplayer; 56.26 +#endif 56.27 +}; 56.28 + 56.29 +struct LJMusic *LJMusic_new(void) { 56.30 + struct LJMusic *m = malloc(sizeof(struct LJMusic)); 56.31 + if (!m) { 56.32 + return NULL; 56.33 + } 56.34 +#if LJMUSIC_USING_VORBIS 56.35 + m->ogg = NULL; 56.36 +#endif 56.37 +#if LJMUSIC_USING_DUMB 56.38 + m->duh = NULL; 56.39 + m->duhplayer = NULL; 56.40 +#endif 56.41 + return m; 56.42 +} 56.43 + 56.44 +void LJMusic_delete(struct LJMusic *m) { 56.45 + if (!m) { 56.46 + return; 56.47 + } 56.48 + LJMusic_unload(m); 56.49 + free(m); 56.50 +} 56.51 + 56.52 +int LJMusic_load(struct LJMusic *m, const char *filename) { 56.53 + if (!m || !filename) { 56.54 + return -1; 56.55 + } 56.56 + 56.57 + LJMusic_unload(m); 56.58 + const char *ext = get_extension(filename); 56.59 + 56.60 +#if LJMUSIC_USING_VORBIS 56.61 + if (!ustricmp(ext, "ogg")) { 56.62 + m->ogg = LJVorbis_open(filename); 56.63 + return m->ogg ? 1 : 0; 56.64 + } 56.65 +#endif 56.66 + 56.67 +#if LJMUSIC_USING_DUMB 56.68 + if (!dumb_inited) { 56.69 + atexit(dumb_exit); 56.70 + dumb_register_stdfiles(); 56.71 + } 56.72 + if (!ustricmp(ext, "it")) { 56.73 + m->duh = dumb_load_it_quick(filename); 56.74 + return m->duh ? 1 : 0; 56.75 + } 56.76 + if (!ustricmp(ext, "xm")) { 56.77 + m->duh = dumb_load_xm_quick(filename); 56.78 + return m->duh ? 1 : 0; 56.79 + } 56.80 + if (!ustricmp(ext, "s3m")) { 56.81 + m->duh = dumb_load_s3m_quick(filename); 56.82 + return m->duh ? 1 : 0; 56.83 + } 56.84 + if (!ustricmp(ext, "mod")) { 56.85 + m->duh = dumb_load_mod_quick(filename); 56.86 + return m->duh ? 1 : 0; 56.87 + } 56.88 +#endif 56.89 + 56.90 + return 0; 56.91 +} 56.92 + 56.93 +void LJMusic_unload(struct LJMusic *m) { 56.94 + if (!m) { 56.95 + return; 56.96 + } 56.97 + LJMusic_stop(m); 56.98 + 56.99 +#if LJMUSIC_USING_DUMB 56.100 + if (m->duh) { 56.101 + unload_duh(m->duh); 56.102 + m->duh = NULL; 56.103 + } 56.104 +#endif 56.105 + 56.106 +#if LJMUSIC_USING_VORBIS 56.107 + if (m->ogg) { 56.108 + LJVorbis_close(m->ogg); 56.109 + m->ogg = NULL; 56.110 + } 56.111 +#endif 56.112 + 56.113 +} 56.114 + 56.115 +void LJMusic_setLoop(struct LJMusic *m, unsigned long int start) { 56.116 +#if LJMUSIC_USING_VORBIS 56.117 + if (m->ogg) { 56.118 + LJVorbis_setLoop(m->ogg, start); 56.119 + } 56.120 +#endif 56.121 +} 56.122 + 56.123 +void LJMusic_start(struct LJMusic *m, int bufferSize, int vol) { 56.124 + if (!m) { 56.125 + return; 56.126 + } 56.127 + 56.128 + LJMusic_stop(m); 56.129 + 56.130 +#if LJMUSIC_USING_DUMB 56.131 + if (m->duh) { 56.132 + m->duhplayer = al_start_duh(m->duh, 2, 0, 56.133 + vol / 256.0, 56.134 + bufferSize, 56.135 + 48000); 56.136 + } 56.137 +#endif 56.138 +#if LJMUSIC_USING_VORBIS 56.139 + if (m->ogg) { 56.140 + LJVorbis_start(m->ogg, bufferSize, vol, 128); 56.141 + } 56.142 +#endif 56.143 + m->paused = 0; 56.144 +} 56.145 + 56.146 +void LJMusic_stop(struct LJMusic *m) { 56.147 + if (!m) { 56.148 + return; 56.149 + } 56.150 + 56.151 +#if LJMUSIC_USING_DUMB 56.152 + if (m->duhplayer) { 56.153 + al_stop_duh(m->duhplayer); 56.154 + m->duhplayer = NULL; 56.155 + } 56.156 +#endif 56.157 + 56.158 +#if LJMUSIC_USING_VORBIS 56.159 + LJVorbis_stop(m->ogg); 56.160 +#endif 56.161 +} 56.162 + 56.163 +void LJMusic_poll(struct LJMusic *m) { 56.164 + if (!m) { 56.165 + return; 56.166 + } 56.167 + 56.168 +#if LJMUSIC_USING_DUMB 56.169 + if (m->duhplayer) { 56.170 + al_poll_duh(m->duhplayer); 56.171 + } 56.172 +#endif 56.173 + 56.174 +#if LJMUSIC_USING_VORBIS 56.175 + if (m->ogg) { 56.176 + LJVorbis_poll(m->ogg); 56.177 + } 56.178 +#endif 56.179 +} 56.180 + 56.181 +void LJMusic_pause(struct LJMusic *m, int value) { 56.182 + value = value ? 1 : 0; 56.183 + if (!m) { 56.184 + 56.185 + } 56.186 + if (WARNINGS && (value == m->paused)) { 56.187 + alert("LJMusic_pause", "unchanging.", "", "OK", 0, 13, 0); 56.188 + return; 56.189 + } 56.190 +#if LJMUSIC_USING_DUMB 56.191 + if (m->duhplayer) { 56.192 + if (value) { 56.193 + al_pause_duh(m->duhplayer); 56.194 + } else { 56.195 + al_resume_duh(m->duhplayer); 56.196 + } 56.197 + } 56.198 +#endif 56.199 + 56.200 +#if LJMUSIC_USING_VORBIS 56.201 + if (m->ogg) { 56.202 + LJVorbis_pause(m->ogg, value); 56.203 + } 56.204 +#endif 56.205 + m->paused = value; 56.206 +} 56.207 +
57.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 57.2 +++ b/src/ljmusic.h Fri Mar 13 00:39:12 2009 -0700 57.3 @@ -0,0 +1,22 @@ 57.4 +/* FIXME: add license block */ 57.5 +#ifndef LJMUSIC_H 57.6 +#define LJMUSIC_H 57.7 + 57.8 +#define LJMUSIC_USING_DUMB 1 57.9 +#define LJMUSIC_USING_VORBIS 1 57.10 + 57.11 +typedef struct LJMusic LJMusic; 57.12 + 57.13 +struct LJMusic *LJMusic_new(void); 57.14 +void LJMusic_delete(struct LJMusic *m); 57.15 +int LJMusic_load(struct LJMusic *m, const char *filename); 57.16 +void LJMusic_unload(struct LJMusic *m); 57.17 +void LJMusic_start(struct LJMusic *m, int bufferSize, int vol); 57.18 +void LJMusic_setLoop(struct LJMusic *m, unsigned long int start); 57.19 +void LJMusic_stop(struct LJMusic *m); 57.20 +void LJMusic_poll(struct LJMusic *m); 57.21 +void LJMusic_pause(struct LJMusic *m, int value); 57.22 + 57.23 + 57.24 +#endif 57.25 +
58.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 58.2 +++ b/src/ljpath.c Fri Mar 13 00:39:12 2009 -0700 58.3 @@ -0,0 +1,294 @@ 58.4 +/* 58.5 +ljpath - functions to support an application that can be 58.6 +installed to either a read-write folder ("portable" config) 58.7 +or to a read-only folder ("installed" config) 58.8 + 58.9 +Copyright 2008 Damian Yerrick 58.10 + 58.11 +Insert zlib license here. 58.12 + 58.13 +*/ 58.14 + 58.15 + 58.16 +#include <stdlib.h> 58.17 +#include <string.h> 58.18 +#include "ljpath.h" 58.19 +#include <limits.h> // for PATH_MAX 58.20 +#include <sys/stat.h> // for mkdir() 58.21 +#include <errno.h> 58.22 + 58.23 +#ifdef WIN32 58.24 +#include <shlobj.h> 58.25 +#include <direct.h> 58.26 +#define USE_WINDOWS_APPDATA 58.27 +#endif 58.28 + 58.29 +// on the DS 58.30 +#ifdef ARM9 58.31 +#include <fat.h> // Chishm's libfat 58.32 +static char hasFAT; 58.33 +#include <unistd.h> // for MAXPATHLEN 58.34 +#else 58.35 +#define hasFAT 1 58.36 +#endif 58.37 + 58.38 +// http://forum.gbadev.org/viewtopic.php?p=147795#147795 58.39 +#if !defined(PATH_MAX) && defined(MAXPATHLEN) 58.40 +#define PATH_MAX MAXPATHLEN 58.41 +#endif 58.42 + 58.43 +static char readWriteFolder[PATH_MAX]; 58.44 +static char skinFolder[PATH_MAX]; 58.45 +static char readOnlyFolder[PATH_MAX]; 58.46 + 58.47 + 58.48 +#ifdef WIN32 58.49 +static int ljmkdir(const char *path) { 58.50 + //allegro_message("mkdir(\"%s\")\n", path); 58.51 + return _mkdir(path); 58.52 +} 58.53 +#else 58.54 +static int ljmkdir(const char *path) { 58.55 + //allegro_message("mkdir(\"%s\")\n", path); 58.56 + return mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); 58.57 +} 58.58 +#endif 58.59 + 58.60 +/** 58.61 + * Wrapper around mkdir() that doesn't error if the 58.62 + * directory already exists. 58.63 + */ 58.64 +static int makeFolder(const char *path) { 58.65 + if (ljmkdir(path) < 0) { 58.66 + if (errno != EEXIST) { 58.67 + return -1; 58.68 + } 58.69 + } 58.70 + return 0; 58.71 +} 58.72 + 58.73 + 58.74 + 58.75 +/** 58.76 + * Computes the name of the folder containing filename. 58.77 + * @param dst destination to receive the folder name, of size PATH_MAX 58.78 + * @param filename name of a file in this folder 58.79 + */ 58.80 +static void ljpathSetFolder(char *dst, const char *filename) { 58.81 + const char *lastPathSep = NULL; 58.82 + 58.83 + // I considered strrchr, but it would need two passes: 58.84 + // one for '/' and one for the drive-letter ':' under Windows. 58.85 + for (const char *here = filename; *here != 0; ++here) { 58.86 + if (*here == '/' || *here == '\\' || *here == ':') { 58.87 + lastPathSep = here; 58.88 + } 58.89 + } 58.90 + if (lastPathSep == NULL || lastPathSep - filename > PATH_MAX - 1) { 58.91 + strcpy(dst, "."); 58.92 + } else { 58.93 + memcpy(dst, filename, lastPathSep - filename); 58.94 + dst[lastPathSep - filename] = 0; 58.95 + } 58.96 +} 58.97 + 58.98 +#if defined(USE_WINDOWS_APPDATA) 58.99 +/** 58.100 + * Retrieves the read/write path on a Windows system. 58.101 + * SHGetFolderPath() is described in 58.102 + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/functions/shgetfolderpath.asp 58.103 + */ 58.104 +static void ljpathSetRWFolder(void) { 58.105 + static const char suffix1[] = "/Pin Eight"; 58.106 + static const char suffix2[] = "/lockjaw"; 58.107 + char path[MAX_PATH + sizeof(suffix1) + sizeof(suffix2)] = ""; 58.108 + 58.109 + int rVal = SHGetFolderPath( 58.110 + NULL, // owner window used with obscure dial-up functionality 58.111 + CSIDL_APPDATA, // type of folder to retrieve 58.112 + NULL, // user ID when impersonating another user 58.113 + 0, // get the current path, not the default path 58.114 + path 58.115 + ); 58.116 + if (rVal) { 58.117 + return; 58.118 + } 58.119 + 58.120 + strcat(path, suffix1); 58.121 + ljmkdir(path); 58.122 + strcat(path, suffix2); 58.123 + ljmkdir(path); 58.124 + if (strlen(path) >= PATH_MAX) { 58.125 + return; 58.126 + } 58.127 + 58.128 + strncpy(readWriteFolder, path, PATH_MAX - 1); 58.129 + readWriteFolder[PATH_MAX - 1] = 0; 58.130 +} 58.131 +#else 58.132 +/** 58.133 + * Retrieves the read/write path on a UNIX system using the HOME 58.134 + * environment variable. 58.135 + */ 58.136 +static void ljpathSetRWFolder(void) { 58.137 + static const char suffix[] = "/.lockjaw"; 58.138 + const char *value = getenv("HOME"); 58.139 + if (!value) { 58.140 + return; 58.141 + } 58.142 + 58.143 + unsigned int len = strlen(value) + sizeof(suffix); 58.144 + if (len > PATH_MAX) { 58.145 + return; 58.146 + } 58.147 + 58.148 + strcpy(readWriteFolder, value); 58.149 + strcat(readWriteFolder, suffix); 58.150 + ljmkdir(readWriteFolder); 58.151 + //allegro_message("readWriteFolder is now \"%s\"\n", skinFolder); 58.152 +} 58.153 +#endif 58.154 + 58.155 +void ljpathSetSkinFolder(const char *filename) { 58.156 + //allegro_message("ljpathSetFolder(skinFolder, \"%s\")\n", filename); 58.157 + ljpathSetFolder(skinFolder, filename); 58.158 + //allegro_message("skinFolder is now \"%s\"\n", skinFolder); 58.159 +} 58.160 + 58.161 +static int ljpathJoin(char *restrict dst, 58.162 + const char *restrict folder, 58.163 + const char *restrict filename) { 58.164 + size_t len = 0; 58.165 + 58.166 + //allegro_message("ljpathJoin(\"%s\", \"%s\")\n", folder, filename); 58.167 + 58.168 + // If the path is not absolute, copy the folder and the 58.169 + // path separator into the destination string. 58.170 + if (filename[0] != '\\' && filename[0] != '\\' 58.171 + && filename[1] != ':') { 58.172 + while (len < PATH_MAX - 1 && *folder != 0) { 58.173 + dst[len++] = *folder++; 58.174 + } 58.175 + /* The path separator '/' is standard under Linux and Mac OS X. 58.176 + It also works on MS-DOS and Windows API, notwithstanding 58.177 + certain Microsoft command-line utilities. */ 58.178 + dst[len++] = '/'; 58.179 + while (len < PATH_MAX - 1 && *filename != 0) { 58.180 + dst[len++] = *filename++; 58.181 + } 58.182 + } 58.183 + 58.184 + if (len < PATH_MAX - 1) { 58.185 + dst[len] = 0; 58.186 + //allegro_message("equals \"%s\"\n", dst); 58.187 + return 1; 58.188 + } 58.189 + //allegro_message("failed\n"); 58.190 + return 0; 58.191 +} 58.192 + 58.193 +/** 58.194 + * Determines whether a file exists and is readable. 58.195 + * @param path the path of a file 58.196 + * @return nonzero iff readable 58.197 + */ 58.198 +static int fexists(const char *path) { 58.199 + struct stat st; 58.200 + if (!hasFAT) { 58.201 + return 0; 58.202 + } 58.203 + int rval = stat(path, &st); 58.204 + //allegro_message("stat(\"%s\") returned %d\n", path, rval); 58.205 + return rval == 0; 58.206 +} 58.207 + 58.208 +int ljpathInit(const char *argv0) { 58.209 +#ifdef ARM9 58.210 + hasFAT = fatInitDefault(); 58.211 + if (hasFAT) { 58.212 + if (makeFolder("/data") < 0 || makeFolder("/data/lockjaw") < 0) { 58.213 + hasFAT = 0; 58.214 + } 58.215 + } 58.216 + strcpy(readOnlyFolder, "/data/lockjaw"); 58.217 + strcpy(readWriteFolder, "/data/lockjaw"); 58.218 + strcpy(skinFolder, "/data/lockjaw"); 58.219 + return hasFAT; 58.220 +#else 58.221 + char path[PATH_MAX]; 58.222 + int installed; 58.223 + 58.224 + //allegro_message("argv[0] = \"%s\"", argv0); 58.225 + ljpathSetFolder(readOnlyFolder, argv0); 58.226 + strcpy(readWriteFolder, "."); 58.227 + strcpy(skinFolder, "."); 58.228 + 58.229 + // determine whether "installed.ini" (an empty text file) 58.230 + // exists 58.231 + installed = ljpathJoin(path, readOnlyFolder, "installed.ini") >= 0 58.232 + && fexists(path); 58.233 + if (installed) { 58.234 + ljpathSetRWFolder(); 58.235 + } 58.236 + return installed; 58.237 +#endif 58.238 +} 58.239 + 58.240 +/** 58.241 + * Finds a file name for reading. 58.242 + * @param dst buffer for path, of size PATH_MAX 58.243 + * @param src name of file 58.244 + * @return 0 if not found, nonzero if found 58.245 + */ 58.246 +int ljpathFind_r(char *restrict dst, const char *restrict filename) { 58.247 + if (!hasFAT) { 58.248 + return 0; 58.249 + } 58.250 + if (ljpathJoin(dst, readWriteFolder, filename) >= 0 58.251 + && fexists(dst)) { 58.252 + return 1; 58.253 + } 58.254 + if (ljpathJoin(dst, skinFolder, filename) >= 0 58.255 + && fexists(dst)) { 58.256 + return 1; 58.257 + } 58.258 + if (ljpathJoin(dst, readOnlyFolder, filename) >= 0 58.259 + && fexists(dst)) { 58.260 + return 1; 58.261 + } 58.262 + return 0; 58.263 +} 58.264 + 58.265 +int ljpathFind_w(char *restrict dst, const char *restrict filename) { 58.266 + return ljpathJoin(dst, readWriteFolder, filename); 58.267 +} 58.268 + 58.269 +FILE *ljfopen(const char *restrict filename, const char *restrict mode) { 58.270 + char path[PATH_MAX]; 58.271 + FILE *fp; 58.272 + 58.273 + if (!hasFAT) { 58.274 + return NULL; 58.275 + } 58.276 + 58.277 + //allegro_message("ljfopen(\"%s\", \"%s\")\n", filename, mode); 58.278 + if (mode[0] == 'r') { 58.279 + 58.280 + // For read-only files, search all three paths. 58.281 + if (ljpathFind_r(path, filename) >= 0 58.282 + && (fp = fopen(path, mode)) != 0) { 58.283 + //allegro_message("fopen(\"%s\", \"%s\") for reading\n", path, mode); 58.284 + return fp; 58.285 + } 58.286 + } else { 58.287 + 58.288 + // For read/write files, check only the read/write path. 58.289 + if (ljpathFind_w(path, filename) >= 0 58.290 + && (fp = fopen(path, mode)) != 0) { 58.291 + //allegro_message("fopen(\"%s\", \"%s\") for writing\n", path, mode); 58.292 + return fp; 58.293 + } 58.294 + } 58.295 + return NULL; 58.296 +} 58.297 +
59.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 59.2 +++ b/src/ljpath.h Fri Mar 13 00:39:12 2009 -0700 59.3 @@ -0,0 +1,74 @@ 59.4 +/* 59.5 +ljpath - functions to support an application that can be 59.6 +installed to either a read-write folder ("portable" config) 59.7 +or to a read-only folder ("installed" config) 59.8 + 59.9 +Copyright 2008 Damian Yerrick 59.10 + 59.11 +Insert zlib license here. 59.12 + 59.13 +*/ 59.14 + 59.15 +#ifndef LJPATH_H 59.16 +#define LJPATH_H 59.17 + 59.18 +#ifdef __cplusplus 59.19 +#include <cstdio> 59.20 +using std::FILE; 59.21 +extern "C" 59.22 +{ 59.23 +#define DISTINCT 59.24 +#else 59.25 +#include <stdio.h> 59.26 +#define DISTINCT restrict 59.27 +#endif 59.28 + 59.29 +/** 59.30 + * Sets up the paths used by ljfopen(). 59.31 + * Determines whether the program is marked as "installed", by 59.32 + * the presence of a file called installed.ini in the folder 59.33 + * containing the executable file. If so, uses a folder in the 59.34 + * user's home directory instead of the current directory for 59.35 + * writable files. 59.36 + * @param argv0 the executable file's path 59.37 + * @return nonzero for installed; zero for portable 59.38 + */ 59.39 +int ljpathInit(const char *argv0); 59.40 + 59.41 +/** 59.42 + * Sets the skin folder to the folder containing a file. For instance, 59.43 + * in a skinnable falling block game, this would be the folder holding 59.44 + * the .skin file that describes the path to each graphic used for the 59.45 + * game display. 59.46 + * @param filename the name of the file 59.47 + */ 59.48 +void ljpathSetSkinFolder(const char *filename); 59.49 + 59.50 +/** 59.51 + * Searches for a file in read-write, skin, and read-only folders 59.52 + * @param dst pointer to a PATH_MAX-byte buffer to hold the path 59.53 + * @param filename the name of the file that will be searched for 59.54 + * @return nonzero if the file was found; 0 if not found 59.55 + */ 59.56 +int ljpathFind_r(char *DISTINCT dst, const char *DISTINCT filename); 59.57 + 59.58 +int ljpathFind_w(char *DISTINCT dst, const char *DISTINCT filename); 59.59 + 59.60 +/** 59.61 + * Searches for a file and opens it. After it is opened, the 59.62 + * caller may use stdio.h operations on it and must close it. 59.63 + * Files being read are searched for using ljpathFind_r; others are 59.64 + * searched for using ljpathFind_r 59.65 + * @param filename the name of the file that will be searched for 59.66 + * @param mode the stdio mode (r, w, a, rb, wb, ab) 59.67 + * @return a magic cookie suitable for passing to stdio.h if the file 59.68 + * was opened; 0 if not opened 59.69 + */ 59.70 +FILE *ljfopen(const char *DISTINCT filename, const char *DISTINCT mode); 59.71 + 59.72 + 59.73 +#ifdef __cplusplus 59.74 +} 59.75 +#endif 59.76 + 59.77 +#endif
60.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 60.2 +++ b/src/ljpc.c Fri Mar 13 00:39:12 2009 -0700 60.3 @@ -0,0 +1,2156 @@ 60.4 +/* PC frontend for LOCKJAW, an implementation of the Soviet Mind Game 60.5 + 60.6 +Copyright (C) 2006-2008 Damian Yerrick <tepples+lj@spamcop.net> 60.7 + 60.8 +This work is free software; you can redistribute it and/or modify 60.9 +it under the terms of the GNU General Public License as published by 60.10 +the Free Software Foundation; either version 2 of the License, or 60.11 +(at your option) any later version. 60.12 + 60.13 +This program is distributed in the hope that it will be useful, 60.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 60.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 60.16 +GNU General Public License for more details. 60.17 + 60.18 +You should have received a copy of the GNU General Public License 60.19 +along with this program; if not, write to the Free Software 60.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 60.21 + 60.22 +Original game concept and design by Alexey Pajitnov. 60.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 60.24 +or The Tetris Company LLC. 60.25 + 60.26 +*/ 60.27 + 60.28 +#include "ljpc.h" 60.29 +#include "ljreplay.h" 60.30 +#include <jpgalleg.h> 60.31 +#include <time.h> 60.32 +#include "scenario.h" 60.33 +#include "ljpath.h" 60.34 + 60.35 +#if 1 60.36 + #define LJ_VERSION "0.46a ("__DATE__")" 60.37 +#else 60.38 + #define LJ_VERSION "WIP ("__DATE__")" 60.39 +#endif 60.40 + 60.41 +#define USE_PICS_FOLDER 0 60.42 +#define MAX_PLAYERS 2 60.43 +#define LJ_TICK_RATE 3600 // frames per minute 60.44 + 60.45 +int bgColor, fgColor = 0, hiliteColor, pfBgColor = 0, pfFgColor; 60.46 +static volatile int curTime = 0; 60.47 +volatile char redrawWholeScreen = 1; 60.48 +static volatile int wantPause = 0; 60.49 +const DATAFILE *dat = NULL, *sound_dat = NULL; 60.50 +const FONT *aver32 = NULL, *aver16 = NULL; 60.51 +char withSound = 0; 60.52 +char autoPause; 60.53 +int refreshRate = 0; 60.54 +char demoFilename[512]; 60.55 + 60.56 +int withPics = -1; 60.57 + 60.58 +char skinName[PATH_MAX] = "default.skin"; 60.59 +char ljblocksSRSName[PATH_MAX]; 60.60 +char ljblocksSegaName[PATH_MAX]; 60.61 +char ljconnSRSName[PATH_MAX]; 60.62 +char ljconnSegaName[PATH_MAX]; 60.63 +char ljbgName[PATH_MAX]; 60.64 +char menubgName[PATH_MAX]; 60.65 +char bgmName[PATH_MAX]; 60.66 +char bgmRhythmName[PATH_MAX]; 60.67 +unsigned long int bgmLoopPoint = 0; 60.68 +int bgmReadyGo = 0; 60.69 +int bgmVolume = 128; 60.70 +int skinW = 800; 60.71 +int skinH = 600; 60.72 + 60.73 + 60.74 + 60.75 + 60.76 +static void incCurTime(void) { 60.77 + ++curTime; 60.78 +} END_OF_FUNCTION(incCurTime); 60.79 + 60.80 +static void amnesia(void) { 60.81 + redrawWholeScreen = 1; 60.82 +} END_OF_FUNCTION(amnesia); 60.83 + 60.84 +static void requestPause(void) { 60.85 + wantPause |= autoPause; 60.86 +} 60.87 + 60.88 + 60.89 +int getTime(void) { 60.90 + return curTime; 60.91 +} 60.92 + 60.93 +void yieldCPU(void) { 60.94 + rest(5); 60.95 +} 60.96 + 60.97 +void ljBeginDraw(LJView *v, int sync) { 60.98 + vsync(); 60.99 + if (redrawWholeScreen) { 60.100 + redrawWholeScreen = 0; 60.101 + playRedrawScreen(v); 60.102 + } 60.103 + 60.104 + BITMAP *sc = v->plat->skin->fullback; 60.105 + 60.106 + // Draw shape 60.107 + blit(v->plat->skin->bg, sc, 0, 0, 0, 0, 48, 16); 60.108 + if (v->control->replayDst) { 60.109 + 60.110 + // circle: record 60.111 + circlefill(sc, 8, 8, 7, fgColor); 60.112 + textout_ex(sc, aver16, "Rec", 18, 2, fgColor, -1); 60.113 + } else if (v->control->replaySrc) { 60.114 + 60.115 + // triangle: play 60.116 + for (int wid = 0; wid < 7; ++wid) { 60.117 + hline(sc, 2, 2 + wid, wid * 2 + 3, fgColor); 60.118 + hline(sc, 2, 14 - wid, wid * 2 + 3, fgColor); 60.119 + } 60.120 + textout_ex(sc, aver16, "Play", 18, 2, fgColor, -1); 60.121 + } else { 60.122 + 60.123 + // square: stop 60.124 + rectfill(sc, 2, 2, 14, 14, fgColor); 60.125 + } 60.126 + blit(sc, screen, 0, 0, 0, 0, 48, 16); 60.127 +} 60.128 + 60.129 +void ljEndDraw(LJView *v) { 60.130 + 60.131 +} 60.132 + 60.133 +static void startRecording(LJView *v) { 60.134 + withPics = -1; 60.135 + 60.136 + // close existing playback 60.137 + if (v->control->replaySrc) { 60.138 + replayClose(v->control->replaySrc); 60.139 + v->control->replaySrc = NULL; 60.140 + } 60.141 + 60.142 + // toggle recording 60.143 + if (v->control->replayDst) { 60.144 + replayClose(v->control->replayDst); 60.145 + v->control->replayDst = NULL; 60.146 + } else { 60.147 + char path[PATH_MAX]; 60.148 + 60.149 + ljpathFind_w(path, "demo.ljm"); 60.150 + v->control->replayDst = newReplay(path, v->field); 60.151 +#if USE_PICS_FOLDER 60.152 + withPics = 0; 60.153 +#endif 60.154 + } 60.155 +} 60.156 + 60.157 +static void startPlaying(LJView *v) { 60.158 + withPics = -1; 60.159 + 60.160 + // close existing recording 60.161 + if (v->control->replayDst) { 60.162 + replayClose(v->control->replayDst); 60.163 + v->control->replayDst = NULL; 60.164 + } 60.165 + 60.166 + // toggle playback 60.167 + if (v->control->replaySrc) { 60.168 + replayClose(v->control->replaySrc); 60.169 + v->control->replaySrc = NULL; 60.170 + } else { 60.171 + char path[PATH_MAX]; 60.172 + 60.173 + if (ljpathFind_r(path, "demo.ljm")) { 60.174 + v->control->replaySrc = openReplay(path, v->field); 60.175 + } 60.176 + v->nLockTimes = 0; 60.177 +#if USE_PICS_FOLDER 60.178 + withPics = 0; 60.179 +#endif 60.180 + } 60.181 + v->backDirty = ~0; 60.182 + v->field->reloaded = 1; 60.183 +} 60.184 + 60.185 +int ljHandleConsoleButtons(LJView *v) { 60.186 + int canceled = 0; 60.187 + 60.188 + while (keypressed()) { 60.189 + int scancode; 60.190 + int codepoint = ureadkey(&scancode); 60.191 + 60.192 + if (scancode == KEY_ESC) { 60.193 + wantPause = 1; 60.194 + } else if (scancode == KEY_PRTSCR) { 60.195 + saveScreen(-1); 60.196 + save_bitmap("ljbackbuf.bmp", v->plat->skin->fullback, NULL); 60.197 + } else if (codepoint == '[') { 60.198 + v->plat->wantRecord = 1; 60.199 + } else if (codepoint == ']') { 60.200 + v->plat->wantRecord = 0; 60.201 + startPlaying(v); 60.202 + } 60.203 + } 60.204 + if (v->plat->wantRecord) { 60.205 + v->plat->wantRecord = 0; 60.206 + startRecording(v); 60.207 + } 60.208 + if (wantPause) { 60.209 + canceled = pauseGame(v->plat); 60.210 + wantPause = 0; 60.211 + } 60.212 + 60.213 + if (wantsClose) { 60.214 + canceled = 1; 60.215 + } 60.216 + 60.217 + return canceled; 60.218 +} 60.219 + 60.220 + 60.221 +static void drawBlock(const LJPCView *const v, int x, int y, int blk) { 60.222 + 60.223 + if (x >= 0 && x < LJ_PF_WID && y >= 0 && y < LJ_PF_VIS_HT) { 60.224 + const int w = v->skin->blkW; 60.225 + const int h = v->skin->blkH; 60.226 + const int dstX = w * x; 60.227 + const int dstY = h * (LJ_PF_VIS_HT - 1 - y); 60.228 + 60.229 + if (v->skin->transparentPF && (blk == 0 || blk == 0x100)) { 60.230 + 60.231 + // Copy background 60.232 + const unsigned int srcX = dstX + v->baseX; 60.233 + const unsigned int srcY = dstY + v->skin->baseY 60.234 + - LJ_PF_VIS_HT * h - v->skin->pfElev; 60.235 + blit(v->skin->bg, v->back, srcX, srcY, dstX, dstY, w, h); 60.236 + } else if (blk && v->skin->connBlocks) { 60.237 + 60.238 + // Copy connected block 60.239 + const unsigned int srcX = ((blk >> 0) & 15) * w; 60.240 + const unsigned int srcY = ((blk >> 4) & 15) * h; 60.241 + blit(v->skin->connBlocks, v->back, srcX, srcY, dstX, dstY, w, h); 60.242 + } else { 60.243 + 60.244 + // Copy lone block 60.245 + const unsigned int srcX = ((blk >> 4) & 7) * w; 60.246 + const unsigned int srcY = ((blk >> 7) & 1) * h; 60.247 + blit(v->skin->blocks, v->back, srcX, srcY, dstX, dstY, w, h); 60.248 + } 60.249 + 60.250 + // 0x100: hotline 60.251 + if (blk & 0x100) { 60.252 + hline(v->back, dstX, dstY + h / 2 - 1, dstX + w - 1, pfFgColor); 60.253 + hline(v->back, dstX, dstY + h / 2 , dstX + w - 1, pfFgColor); 60.254 + hline(v->back, dstX, dstY + h / 2 + 1, dstX + w - 1, pfBgColor); 60.255 + } 60.256 + } 60.257 +} 60.258 + 60.259 +/** 60.260 + * Draws multiple rows of the playfield 60.261 + * @param p the playfield 60.262 + * @param rows the rows to be updated (0x00001 on bottom, 0x80000 on top) 60.263 + */ 60.264 +void updField(const LJView *const v, LJBits rows) { 60.265 + const LJField *const p = v->field; 60.266 + 60.267 + acquire_bitmap(v->plat->back); 60.268 + for (int y = 0; 60.269 + y < LJ_PF_VIS_HT && rows != 0; 60.270 + ++y, rows >>= 1) { 60.271 + int blankTile = 0; 60.272 + 60.273 + // hotline: draw 0x100 as the background 60.274 + if (hotlineRows[y] && v->field->scoreStyle == LJSCORE_HOTLINE) { 60.275 + blankTile = 0x100; 60.276 + } 60.277 + // during line clear delay, draw bars to show which lines were cleared 60.278 + if (p->state == LJS_LINES_FALLING && p->stateTime > 0 60.279 + && ((1 << y) & p->tempRows)) { 60.280 + blankTile = 0x100; 60.281 + } 60.282 + if (rows & 1) { 60.283 + for (int x = p->leftWall; x < p->rightWall; x++) { 60.284 + int b = v->hidePF ? 0 : p->b[y][x]; 60.285 + drawBlock(v->plat, x, y, b ? b : blankTile); 60.286 + } 60.287 + } 60.288 + } 60.289 + release_bitmap(v->plat->back); 60.290 +} 60.291 + 60.292 + 60.293 +#define SHADOW_BLOCK 0x00 60.294 + 60.295 +/** 60.296 + * Draws a tetromino whose lower left corner of the bounding box is at (x, y) 60.297 + * @param b the bitmap to draw to 60.298 + * @param piece the piece to be drawn 60.299 + * @param x distance from to left side of 4x4 box 60.300 + * @param y distance from top of bitmap to bottom of 4x4 box 60.301 + * @param the rotation state (0: U; 1: R; 2: D; 3: L; 4: Initial position) 60.302 + * @param w width of each block 60.303 + * @param h height of each block 60.304 + * @param color Drawing style 60.305 + * color == 0: draw shadow 60.306 + * color == 0x10 through 0x70: draw in that color 60.307 + * color == 0x80: draw as garbage 60.308 + * color == -255 through -1: draw with 255 through 1 percent lighting 60.309 + */ 60.310 +LJBits drawPiece(LJView *const v, BITMAP *const b, 60.311 + int piece, int x, int y, int theta, 60.312 + int color, int w, int h) { 60.313 + // Don't try to draw the -1 that's the sentinel for no hold piece 60.314 + if (piece < 0) 60.315 + return 0; 60.316 + 60.317 + LJBits rows = 0; 60.318 + LJBlkSpec blocks[4]; 60.319 + const int srcW = v->plat->skin->blkW; 60.320 + const int srcH = v->plat->skin->blkH; 60.321 + BITMAP *singleBlocks = v->plat->skin->blocks; 60.322 + int singleSeries = (color == 0 60.323 + && singleBlocks->h >= 6 * srcH) 60.324 + ? 4 : 2; 60.325 + 60.326 + // color: 0 for shadow, >0 for piece 60.327 + BITMAP *connBlocks = (color != SHADOW_BLOCK) 60.328 + ? v->plat->skin->connBlocks : NULL; 60.329 + 60.330 + BITMAP *transTemp = color < 0 60.331 + || (color == SHADOW_BLOCK && v->hideShadow < LJSHADOW_COLORED) 60.332 + ? create_bitmap(w, h) : 0; 60.333 + 60.334 + if (transTemp) { 60.335 + set_trans_blender(0, 0, 0, v->hideShadow ? 128 : 64); 60.336 + } 60.337 + 60.338 + expandPieceToBlocks(blocks, v->field, piece, 0, 0, theta); 60.339 + acquire_bitmap(b); 60.340 + for (int blk = 0; blk < 4; ++blk) { 60.341 + int blkValue = blocks[blk].conn; 60.342 + 60.343 + // If blkValue == 0 then the block is not part of the piece, 60.344 + // such as if it's a domino or tromino or (once pentominoes 60.345 + // are added) a tetromino. 60.346 + if (blkValue) { 60.347 + int blkX = blocks[blk].x; 60.348 + int blkY = blocks[blk].y; 60.349 + const int dstX = x + w * blkX; 60.350 + const int dstY = y + h * (-1 - blkY); 60.351 + 60.352 + if (color > 0) { 60.353 + blkValue = color | (blkValue & CONNECT_MASK); 60.354 + } else if (color == 0 && v->hideShadow == LJSHADOW_COLORLESS) { 60.355 + blkValue = 0; 60.356 + } 60.357 + 60.358 + if (connBlocks) { 60.359 + const unsigned int srcX = ((blkValue >> 0) & 15) * srcW; 60.360 + const unsigned int srcY = ((blkValue >> 4) & 15) * srcH; 60.361 + 60.362 + if (transTemp) { 60.363 + stretch_blit(connBlocks, transTemp, 60.364 + srcX, srcY, srcW, srcH, 60.365 + 0, 0, w, h); 60.366 + if (color < 0) { 60.367 + draw_lit_sprite(b, transTemp, dstX, dstY, color + 256); 60.368 + } else { 60.369 + draw_trans_sprite(b, transTemp, dstX, dstY); 60.370 + } 60.371 + } else if (srcW != w || srcH != h) { 60.372 + stretch_blit(connBlocks, b, 60.373 + srcX, srcY, srcW, srcH, 60.374 + dstX, dstY, w, h); 60.375 + if (dstY > 600) { 60.376 + allegro_message("dstY = %d\n", dstY); 60.377 + } 60.378 + } else { 60.379 + blit(connBlocks, b, 60.380 + srcX, srcY, 60.381 + dstX, dstY, w, h); 60.382 + } 60.383 + } else { 60.384 + const unsigned int srcX = ((blkValue >> 4) & 7) * srcW; 60.385 + const unsigned int srcY = (((blkValue >> 7) & 1) + singleSeries) * srcH; 60.386 + 60.387 + if (transTemp) { 60.388 + stretch_blit(singleBlocks, transTemp, 60.389 + srcX, srcY, srcW, srcH, 60.390 + 0, 0, w, h); 60.391 + if (color < 0) { 60.392 + draw_lit_sprite(b, transTemp, dstX, dstY, color + 256); 60.393 + } else { 60.394 + draw_trans_sprite(b, transTemp, dstX, dstY); 60.395 + } 60.396 + } else { 60.397 + stretch_blit(singleBlocks, b, 60.398 + srcX, srcY, srcW, srcH, 60.399 + dstX, dstY, w, h); 60.400 + } 60.401 + } 60.402 + rows |= 1 << blkY; 60.403 + } 60.404 + } 60.405 + release_bitmap(b); 60.406 + if (transTemp) { 60.407 + solid_mode(); 60.408 + destroy_bitmap(transTemp); 60.409 + } 60.410 + 60.411 + return rows; 60.412 +} 60.413 + 60.414 +void drawPieceInPF(LJView *v, int dstX, LJFixed dstY, int theta, int color) { 60.415 + LJBits bits = 0; 60.416 + BITMAP *b = v->plat->back; 60.417 + const LJField *const p = v->field; 60.418 + int y = ljfixfloor(dstY); 60.419 + const int w = v->plat->skin->blkW; 60.420 + const int h = v->plat->skin->blkH; 60.421 + int drawnY = fixmul(h, dstY); 60.422 + 60.423 + bits = drawPiece(v, b, p->curPiece[0], 60.424 + w * dstX, h * LJ_PF_VIS_HT - drawnY, 60.425 + theta, 60.426 + color, w, h); 60.427 + bits = (y >= 0) ? bits << y : bits >> -y; 60.428 + bits &= (1 << LJ_PF_VIS_HT) - 1; 60.429 + if (dstY & 0xFFFF) { 60.430 + bits |= bits << 1; 60.431 + } 60.432 + 60.433 + v->backDirty |= bits; 60.434 + v->frontDirty |= bits; 60.435 +} 60.436 + 60.437 + 60.438 +void drawFallingPiece(LJView *v) { 60.439 + const LJField *const p = v->field; 60.440 + int piece = p->curPiece[0]; 60.441 + int y = v->smoothGravity 60.442 + ? p->y 60.443 + : ljitofix(ljfixfloor(p->y)); 60.444 + const int color = (p->state == LJS_LANDED) 60.445 + ? -128 - ((p->stateTime + 1) * 128 / (p->speed.lockDelay + 1)) 60.446 + : pieceColors[piece]; 60.447 + 60.448 + // Draw trails 60.449 + if (v->showTrails) { 60.450 + 60.451 + // trail going up 60.452 + while (v->trailY - y < ljitofix(-1)) { 60.453 + v->trailY += ljitofix(1); 60.454 + drawPieceInPF(v, p->x, v->trailY, p->theta, color); 60.455 + } 60.456 + 60.457 + // trail going down 60.458 + while (v->trailY - y > ljitofix(1)) { 60.459 + v->trailY -= ljitofix(1); 60.460 + drawPieceInPF(v, p->x, v->trailY, p->theta, color); 60.461 + } 60.462 + } 60.463 + drawPieceInPF(v, p->x, y, p->theta, color); 60.464 + v->trailY = y; 60.465 +} 60.466 + 60.467 +void drawShadow(LJView *v) { 60.468 + const LJField *const p = v->field; 60.469 + int y = ljitofix(p->hardDropY); 60.470 + const int color = SHADOW_BLOCK; 60.471 + drawPieceInPF(v, p->x, y, p->theta, color); 60.472 +} 60.473 + 60.474 +void drawNextPieces(LJView *v) { 60.475 + int baseX = v->plat->baseX; 60.476 + int baseY = v->plat->skin->baseY - v->plat->skin->pfElev; 60.477 + int blkW = v->plat->skin->blkW; 60.478 + int blkH = v->plat->skin->blkH; 60.479 + int holdPieceColor = v->field->alreadyHeld 60.480 + ? 0x80 60.481 + : pieceColors[v->field->holdPiece]; 60.482 + int ceil = v->field->ceiling; 60.483 + 60.484 + BITMAP *sc = v->plat->skin->fullback; 60.485 + 60.486 + if (v->frontDirty & LJ_DIRTY_NEXT) { 60.487 + int holdW = blkW * 2 / 3; 60.488 + int holdH = blkH * 2 / 3; 60.489 + int holdX = baseX + blkW; 60.490 + int holdY = baseY - (ceil - 1) * blkH - 4 * holdH; 60.491 + 60.492 + // Move the hold piece within the screen 60.493 + if (holdY < 0) { 60.494 + holdY = 0; 60.495 + } 60.496 + 60.497 + // Draw hold piece 60.498 + blit(v->plat->skin->bg, sc, 60.499 + holdX, holdY, 60.500 + holdX, holdY, 60.501 + holdW * 4, holdH * 2); 60.502 + drawPiece(v, sc, 60.503 + v->field->holdPiece, holdX, holdY + 4 * holdH, 4, 60.504 + holdPieceColor, holdW, holdH); 60.505 + blit(sc, screen, 60.506 + holdX, holdY, 60.507 + holdX, holdY, 60.508 + holdW * 4, holdH * 2); 60.509 + } 60.510 + // Draw next pieces 60.511 + 60.512 + switch (v->plat->skin->nextPos) { 60.513 + case LJNEXT_RIGHT: 60.514 + case LJNEXT_RIGHT_TAPER: 60.515 + if (v->frontDirty & LJ_DIRTY_NEXT) { 60.516 + int y = baseY - ceil * blkH; 60.517 + int x = baseX + LJ_PF_WID * blkW; 60.518 + int w = blkW; 60.519 + int h = blkH; 60.520 + for (int i = 1; i <= v->nextPieces; ++i) { 60.521 + int piece = v->field->curPiece[i]; 60.522 + 60.523 + blit(v->plat->skin->bg, sc, x, y, x, y, w * 4, h * 2); 60.524 + if (!v->hideNext) { 60.525 + drawPiece(v, sc, 60.526 + piece, x, y + 4 * h, 4, 60.527 + pieceColors[piece], w, h); 60.528 + } 60.529 + blit(sc, screen, x, y, x, y, w * 4, h * 2); 60.530 + y += 8 + h * 2; 60.531 + if (v->plat->skin->nextPos == LJNEXT_RIGHT_TAPER) { 60.532 + --h; 60.533 + --w; 60.534 + } 60.535 + } 60.536 + } 60.537 + break; 60.538 + 60.539 + case LJNEXT_TOP: 60.540 + if (v->frontDirty & LJ_DIRTY_NEXT) { 60.541 + int y = baseY - (ceil + 2) * blkH - 8; 60.542 + int x = baseX + 4 * blkW; 60.543 + int blitX = x, blitY = y; 60.544 + 60.545 + blit(v->plat->skin->bg, sc, x, y, x, y, blkW * 8, blkH * 2); 60.546 + if (!v->hideNext) { 60.547 + if (v->nextPieces >= 1) { 60.548 + int piece = v->field->curPiece[1]; 60.549 + drawPiece(v, sc, 60.550 + piece, x, y + 4 * blkH, 4, 60.551 + pieceColors[piece], blkW, blkH); 60.552 + } 60.553 + if (v->nextPieces >= 2) { 60.554 + int piece = v->field->curPiece[2]; 60.555 + x += 4 * blkW; 60.556 + drawPiece(v, sc, 60.557 + piece, x, y + 3 * blkH, 4, 60.558 + pieceColors[piece], blkW/ 2, blkH / 2); 60.559 + } 60.560 + if (v->nextPieces >= 3) { 60.561 + int piece = v->field->curPiece[3]; 60.562 + x += 2 * blkW; 60.563 + drawPiece(v, sc, 60.564 + piece, x, y + 3 * blkH, 4, 60.565 + pieceColors[piece], blkW / 2, blkH / 2); 60.566 + } 60.567 + } 60.568 + blit(sc, screen, blitX, blitY, blitX, blitY, blkW * 8, blkH * 2); 60.569 + } 60.570 + break; 60.571 + } 60.572 + 60.573 + if (v->plat->nextAbove && !v->hideNext) { 60.574 + int row = (v->field->hardDropY + 4); 60.575 + int x = (1 + v->field->x) * blkW; 60.576 + for (int i = 1; 60.577 + i <= v->plat->nextAbove 60.578 + && row <= v->field->ceiling - 2; 60.579 + ++i) { 60.580 + int y = (LJ_PF_VIS_HT - row) * blkH; 60.581 + int piece = v->field->curPiece[i]; 60.582 + 60.583 + drawPiece(v, v->plat->back, 60.584 + piece, x, y, 4, 60.585 + pieceColors[piece], blkW / 2, blkH / 2); 60.586 + v->backDirty |= 3 << row; 60.587 + row += 2; 60.588 + } 60.589 + } 60.590 + v->frontDirty &= ~LJ_DIRTY_NEXT; 60.591 +} 60.592 + 60.593 +void drawScore(LJView *v) { 60.594 + int gameTime = v->field->gameTime; 60.595 + int seconds = gameTime / 60; 60.596 + int minutes = seconds / 60; 60.597 + int baseX = v->plat->baseX; 60.598 + int tpm = -1; 60.599 + int spawnLeft = v->plat->skin->blkW * LJ_SPAWN_X + baseX; 60.600 + int pfRight = v->plat->skin->blkW * LJ_PF_WID + baseX; 60.601 + BITMAP *sc = v->plat->skin->fullback; 60.602 + 60.603 + if (withPics >= 0) { 60.604 + if (v->field->nPieces != withPics) { 60.605 + saveScreen(v->field->nPieces); 60.606 + } 60.607 + withPics = v->field->nPieces; 60.608 + } 60.609 + 60.610 + if (v->nLockTimes >= 2 ) { 60.611 + int time = v->lockTime[0] - v->lockTime[v->nLockTimes - 1]; 60.612 + if (time > 0) { 60.613 + tpm = 3600 * (v->nLockTimes - 1) / time; 60.614 + } 60.615 + } 60.616 + 60.617 + if (v->frontDirty & LJ_DIRTY_SCORE) { 60.618 + switch (v->plat->skin->nextPos) { 60.619 + case LJNEXT_TOP: 60.620 + blit(v->plat->skin->bg, sc, 60.621 + pfRight, 72, 60.622 + pfRight, 72, 60.623 + 112, 136); 60.624 + 60.625 + textout_ex(sc, aver32, "Lines:", pfRight, 72, fgColor, -1); 60.626 + textprintf_right_ex(sc, aver32, pfRight + 104, 102, fgColor, -1, 60.627 + "%d", v->field->lines); 60.628 + textout_ex(sc, aver32, "Score:", pfRight, 142, fgColor, -1); 60.629 + textprintf_right_ex(sc, aver32, pfRight + 104, 172, fgColor, -1, 60.630 + "%d", v->field->score); 60.631 + textout_ex(sc, aver32, "Level:", pfRight, 212, fgColor, -1); 60.632 + textprintf_right_ex(sc, aver32, pfRight + 104, 242, fgColor, -1, 60.633 + "%d", v->field->speedState.level); 60.634 + blit(sc, screen, 60.635 + pfRight, 72, 60.636 + pfRight, 72, 60.637 + 112, 136); 60.638 + break; 60.639 + 60.640 + default: 60.641 + blit(v->plat->skin->bg, sc, spawnLeft, 12, spawnLeft, 12, 288, 30); 60.642 + blit(v->plat->skin->bg, sc, 60.643 + spawnLeft, 42, spawnLeft, 42, 60.644 + pfRight - spawnLeft, 30); 60.645 + textprintf_right_ex(sc, aver32, spawnLeft + 288, 12, fgColor, -1, 60.646 + "Lv. %d", v->field->speedState.level); 60.647 + textprintf_ex(sc, aver32, spawnLeft, 12, fgColor, -1, 60.648 + "Lines: %d", v->field->lines); 60.649 + textprintf_ex(sc, aver32, spawnLeft, 42, fgColor, -1, 60.650 + "Score: %d", v->field->score); 60.651 + blit(sc, screen, spawnLeft, 12, spawnLeft, 12, 288, 60); 60.652 + break; 60.653 + } 60.654 + } 60.655 + 60.656 + /* If speed is defined, and there is room to draw it, draw it. */ 60.657 + if (tpm > 0 && v->nextPieces <= 3) { 60.658 + blit(v->plat->skin->bg, sc, 60.659 + pfRight, 282, 60.660 + pfRight, 282, 60.661 + 104, 60); 60.662 + textout_ex(sc, aver32, "Speed:", pfRight, 282, fgColor, -1); 60.663 + textprintf_right_ex(sc, aver32, pfRight + 104, 312, fgColor, -1, 60.664 + "%d", tpm); 60.665 + blit(sc, screen, 60.666 + pfRight, 282, 60.667 + pfRight, 282, 60.668 + 104, 60); 60.669 + } 60.670 + 60.671 + if (v->frontDirty & LJ_DIRTY_NEXT) { 60.672 + 60.673 + // Erase gimmick 60.674 + blit(v->plat->skin->bg, sc, 60.675 + baseX, v->plat->skin->baseY + 8, 60.676 + baseX, v->plat->skin->baseY + 8, 60.677 + v->plat->skin->blkW * LJ_PF_WID, 30); 60.678 + // Draw gimmick 60.679 + if (v->field->gimmick >= 0 && v->field->gimmick < LJGM_N_GIMMICKS) { 60.680 + const char *gimmickName = ljGetFourCCName(gimmickNames[v->field->gimmick]); 60.681 + textout_centre_ex(sc, aver32, 60.682 + gimmickName ? gimmickName : "Bad gimmick ID", 60.683 + baseX + (LJ_PF_WID / 2) * v->plat->skin->blkW, 60.684 + v->plat->skin->baseY + 8, 60.685 + fgColor, -1); 60.686 + blit(sc, screen, 60.687 + baseX, v->plat->skin->baseY + 8, 60.688 + baseX, v->plat->skin->baseY + 8, 60.689 + v->plat->skin->blkW * LJ_PF_WID, 30); 60.690 + } 60.691 + } 60.692 + drawNextPieces(v); 60.693 + 60.694 + blit(v->plat->skin->bg, sc, pfRight, 42, pfRight, 42, 96, 30); 60.695 +#if 0 60.696 + // Use this for DEBUG inspection into a variable 60.697 + textprintf_right_ex(sc, aver16, pfRight + 96, 8, fgColor, -1, 60.698 + "%d", v->field->bpmCounter); 60.699 +#endif 60.700 + textprintf_right_ex(sc, aver32, pfRight + 96, 42, fgColor, -1, 60.701 + "%d:%02d", minutes, seconds - 60 * minutes); 60.702 + blit(sc, screen, pfRight, 42, pfRight, 42, 96, 30); 60.703 + 60.704 +} 60.705 + 60.706 +void blitField(LJView *v) { 60.707 + int blkH = v->plat->skin->blkH; 60.708 + int rowY = v->plat->skin->baseY 60.709 + - v->plat->skin->pfElev 60.710 + - blkH * v->field->ceiling; 60.711 + int x = v->plat->skin->blkW * v->field->leftWall; 60.712 + int w = v->plat->skin->blkW * (v->field->rightWall - v->field->leftWall); 60.713 + 60.714 + // Copy each dirty row 60.715 + for (int y = v->field->ceiling - 1; y >= 0; --y) { 60.716 + if (v->frontDirty & (1 << y)) { 60.717 + int top = (LJ_PF_VIS_HT - y - 1) * blkH; 60.718 + int ht = 0; 60.719 + 60.720 + // Find the height of the contiguous rows to blit 60.721 + do { 60.722 + ht += blkH; 60.723 + --y; 60.724 + } while ((y >= 0) 60.725 + && (v->frontDirty & (1 << y))); 60.726 + blit(v->plat->back, screen, 60.727 + x, top, 60.728 + x + v->plat->baseX, rowY, 60.729 + w, ht); 60.730 + rowY += ht; 60.731 + } 60.732 + rowY += blkH; 60.733 + } 60.734 + 60.735 + v->frontDirty &= (~0) << LJ_PF_HT; 60.736 +} 60.737 + 60.738 +void saveScreen(int n) { 60.739 + BITMAP *b = create_bitmap(SCREEN_W, SCREEN_H); 60.740 + if (b) { 60.741 + PALETTE pal; 60.742 + 60.743 + get_palette(pal); 60.744 + blit(screen, b, 0, 0, 0, 0, SCREEN_W, SCREEN_H); 60.745 + if (n < 0) { 60.746 + save_bitmap("ljsnap.bmp", b, pal); 60.747 + } else { 60.748 + char filename[64]; 60.749 + sprintf(filename, "pics/lj%05d.bmp", n); 60.750 + save_bitmap(filename, b, pal); 60.751 + } 60.752 + destroy_bitmap(b); 60.753 + } 60.754 +} 60.755 + 60.756 +#define PRESETS_PER_COL 6 60.757 +#define N_NONPRESETS 2 60.758 +#define PRESETS_TOP 140 60.759 +#define PRESET_COL_WIDTH 250 60.760 +#define PRESET_ROW_HT 40 60.761 + 60.762 +static const char *const nonPresetNames[N_NONPRESETS] = { 60.763 + "Full Custom", "Back" 60.764 +}; 60.765 + 60.766 +static void getPresetDrawRow(unsigned int preset, int hilite) { 60.767 + unsigned int ht = text_height(aver32); 60.768 + const char *txt = preset < nLoadedPresets 60.769 + ? loadedPresets[preset].name 60.770 + : nonPresetNames[preset - nLoadedPresets]; 60.771 + if (!txt) { 60.772 + txt = "Bad"; 60.773 + } 60.774 + unsigned int wid = text_length(aver32, txt); 60.775 + 60.776 + int buttonCol = preset / PRESETS_PER_COL; 60.777 + int buttonX = 20 + buttonCol * PRESET_COL_WIDTH; 60.778 + int buttonY = PRESETS_TOP 60.779 + + PRESET_ROW_HT * (preset - buttonCol * PRESETS_PER_COL); 60.780 + unsigned int buttonWidth = wid + 16; 60.781 + 60.782 + rectfill(screen, 60.783 + buttonX, buttonY, 60.784 + buttonX + buttonWidth - 1, buttonY + PRESET_ROW_HT - 1, 60.785 + hilite ? hiliteColor : bgColor); 60.786 + if (hilite) { 60.787 + rect(screen, 60.788 + buttonX, buttonY, 60.789 + buttonX + buttonWidth - 1, buttonY + PRESET_ROW_HT - 1, 60.790 + fgColor); 60.791 + } 60.792 + textout_ex(screen, aver32, txt, 60.793 + 8 + buttonX, 60.794 + 20 + buttonY - ht / 2, 60.795 + fgColor, -1); 60.796 +} 60.797 + 60.798 +int getPreset(int lastPreset) { 60.799 + LJBits lastKeys = ~0; 60.800 + redrawWholeScreen = 1; 60.801 + 60.802 + clear_keybuf(); 60.803 + if (lastPreset < 0) { 60.804 + lastPreset += nLoadedPresets + N_NONPRESETS; 60.805 + } 60.806 + 60.807 + for(int done = 0; done == 0; ) { 60.808 + if (redrawWholeScreen) { 60.809 + redrawWholeScreen = 0; 60.810 + clear_to_color(screen, bgColor); 60.811 + textout_ex(screen, aver32, "LOCKJAW > Play", 16, 32, fgColor, -1); 60.812 + textout_ex(screen, aver32, "Select a scenario:", 16, 90, fgColor, -1); 60.813 + 60.814 + for (int preset = 0; 60.815 + preset < nLoadedPresets + N_NONPRESETS; 60.816 + ++preset) { 60.817 + getPresetDrawRow(preset, preset == lastPreset); 60.818 + } 60.819 + textout_ex(screen, aver16, "Arrows: move; Rotate Right: start", 60.820 + 16, PRESETS_TOP + 40 * (PRESETS_PER_COL + 1), fgColor, -1); 60.821 + textout_ex(screen, aver16, "Coming soon: an editor for these!", 60.822 + 16, PRESETS_TOP + 40 * (PRESETS_PER_COL + 1) + 20, fgColor, -1); 60.823 + } 60.824 + while (keypressed()) { 60.825 + int scancode; 60.826 + ureadkey(&scancode); 60.827 + if (scancode == KEY_PRTSCR) { 60.828 + saveScreen(-1); 60.829 + } 60.830 + } 60.831 + 60.832 + int preset = lastPreset; 60.833 + LJBits keys = menuReadPad(); 60.834 + LJBits newKeys = keys & ~lastKeys; 60.835 + 60.836 + if ((newKeys & VKEY_ROTL) || wantsClose) { 60.837 + preset = nLoadedPresets + N_NONPRESETS - 1; 60.838 + ezPlaySample("rotate_wav", 128); 60.839 + } 60.840 + if ((newKeys & VKEY_ROTR) || wantsClose) { 60.841 + done = 1; 60.842 + ezPlaySample("line_wav", 128); 60.843 + } 60.844 + 60.845 + if (newKeys & VKEY_UP) { 60.846 + if (preset <= 0) { 60.847 + preset = nLoadedPresets + N_NONPRESETS - 1; 60.848 + } else { 60.849 + --preset; 60.850 + } 60.851 + ezPlaySample("shift_wav", 128); 60.852 + } 60.853 + if (newKeys & VKEY_DOWN) { 60.854 + ++preset; 60.855 + if (preset >= nLoadedPresets + N_NONPRESETS) { 60.856 + preset = 0; 60.857 + } 60.858 + ezPlaySample("shift_wav", 128); 60.859 + } 60.860 + 60.861 + if (newKeys & VKEY_LEFT) { 60.862 + if (preset < PRESETS_PER_COL) { 60.863 + preset += (((nLoadedPresets + N_NONPRESETS) 60.864 + / PRESETS_PER_COL) 60.865 + ) 60.866 + * PRESETS_PER_COL; 60.867 + if (preset >= nLoadedPresets + N_NONPRESETS) { 60.868 + preset -= PRESETS_PER_COL; 60.869 + } 60.870 + } else { 60.871 + preset -= PRESETS_PER_COL; 60.872 + } 60.873 + ezPlaySample("shift_wav", 128); 60.874 + } 60.875 + if (newKeys & VKEY_RIGHT) { 60.876 + preset += PRESETS_PER_COL; 60.877 + if (preset >= nLoadedPresets + N_NONPRESETS) { 60.878 + preset %= PRESETS_PER_COL; 60.879 + } 60.880 + ezPlaySample("shift_wav", 128); 60.881 + } 60.882 + 60.883 + if (preset != lastPreset) { 60.884 + vsync(); 60.885 + getPresetDrawRow(lastPreset, 0); 60.886 + getPresetDrawRow(preset, 1); 60.887 + lastPreset = preset; 60.888 + } else { 60.889 + rest(30); 60.890 + } 60.891 + lastKeys = keys; 60.892 + } 60.893 + 60.894 + // Wrap the nonpresets into the negative numbers. 60.895 + if (lastPreset >= nLoadedPresets) { 60.896 + lastPreset -= (nLoadedPresets + N_NONPRESETS); 60.897 + } 60.898 + 60.899 + return lastPreset; 60.900 +} 60.901 + 60.902 +/** 60.903 + * Pauses the game, returning nonzero if the player wants to quit. 60.904 + */ 60.905 +int pauseGame(LJPCView *v) { 60.906 + int escCount = 0; 60.907 + int quit = 0; 60.908 + int escHold = curTime; 60.909 + redrawWholeScreen = 1; 60.910 + 60.911 + LJMusic_pause(v->skin->bgm, 1); 60.912 + while (escCount < 2 && !quit) { 60.913 + LJMusic_poll(v->skin->bgm); 60.914 + if (redrawWholeScreen) { 60.915 + redrawWholeScreen = 0; 60.916 + clear_to_color(screen, pfBgColor); 60.917 + textout_centre_ex(screen, aver32, "LOCKJAW: GAME PAUSED", 60.918 + SCREEN_W / 2, 200, pfFgColor, -1); 60.919 + textout_centre_ex(screen, aver32, "Press Esc to continue", 60.920 + SCREEN_W / 2, 250, pfFgColor, -1); 60.921 + textout_centre_ex(screen, aver32, "or hold Esc to quit", 60.922 + SCREEN_W / 2, 300, pfFgColor, -1); 60.923 + } 60.924 + 60.925 + if (key[KEY_ESC]) { 60.926 + if (escHold == 0) { 60.927 + escHold = curTime; 60.928 + } 60.929 + if (curTime - escHold >= 60) { 60.930 + quit = 1; 60.931 + } 60.932 + } else { 60.933 + if (escHold) { 60.934 + ++escCount; 60.935 + } 60.936 + escHold = 0; 60.937 + } 60.938 + rest(30); 60.939 + if (wantsClose) { 60.940 + quit = 1; 60.941 + } 60.942 + 60.943 + } 60.944 + LJMusic_pause(v->skin->bgm, 0); 60.945 + redrawWholeScreen = 1; 60.946 + clear_keybuf(); 60.947 + return quit; 60.948 +} 60.949 + 60.950 + 60.951 +void pcInit(LJView *v, struct LJPrefs *prefs) { 60.952 + v->plat->b2bcd1 = 0; 60.953 + v->plat->b2bcd2 = 0; 60.954 + v->plat->baseX = v->plat->skin->baseX; 60.955 + 60.956 + // If the player has chosen to use more next pieces than the 60.957 + // next piece position can handle, set the number of 60.958 + // next pieces for correctness of debrief(). 60.959 + if (v->nextPieces > 3 && v->plat->skin->nextPos == LJNEXT_TOP) { 60.960 + v->nextPieces = 3; 60.961 + } 60.962 +} 60.963 + 60.964 +/** 60.965 + * Redraws everything on the screen. 60.966 + * Called when needs redraw. 60.967 + */ 60.968 +void playRedrawScreen(LJView *v) { 60.969 + blit(v->plat->skin->bg, screen, 60.970 + 0, 0, 60.971 + 0, 0, 60.972 + SCREEN_W, SCREEN_H); 60.973 + v->frontDirty = ~0; 60.974 +} 60.975 + 60.976 +#if 0 60.977 +void startingAniWantsSkip(LJView *v) { 60.978 + LJInput unusedIn; 60.979 + addKeysToInput(&unusedIn, readPad(), v->field, v->control); 60.980 +} 60.981 +#endif 60.982 + 60.983 +void playSampleForTetromino(int piece); 60.984 + 60.985 +void restPollingMusic(int nFrames, LJMusic *bgm) { 60.986 + nFrames += curTime; 60.987 + while (curTime - nFrames < 0) { 60.988 + if (bgm) { 60.989 + LJMusic_poll(bgm); 60.990 + } 60.991 + } 60.992 +} 60.993 + 60.994 +extern int bgmReadyGo; 60.995 +void startingAnimation(LJView *v) { 60.996 + int readyGoX = v->plat->baseX + v->plat->skin->blkW * LJ_PF_WID / 2; 60.997 + int readyGoY = v->plat->skin->baseY 60.998 + - v->plat->skin->pfElev 60.999 + - v->plat->skin->blkH 60.1000 + * v->field->ceiling * 3 / 5; 60.1001 + 60.1002 + clear_keybuf(); 60.1003 + v->backDirty = 0; 60.1004 + 60.1005 + playRedrawScreen(v); 60.1006 + blitField(v); 60.1007 + textout_centre_ex(screen, aver32, "Ready", 60.1008 + readyGoX, readyGoY, pfFgColor, -1); 60.1009 + 60.1010 + ezPlaySample("ready_wav", 128); 60.1011 + restPollingMusic(36, bgmReadyGo ? v->plat->skin->bgm : NULL); 60.1012 + v->frontDirty = ~0; 60.1013 + 60.1014 + if (!wantsClose) { 60.1015 + blitField(v); 60.1016 + textout_centre_ex(screen, aver32, "GO!", 60.1017 + readyGoX, readyGoY, pfFgColor, -1); 60.1018 + drawScore(v); 60.1019 + 60.1020 + ezPlaySample("go_wav", 128); 60.1021 + v->frontDirty = ~0; 60.1022 + restPollingMusic(12, bgmReadyGo ? v->plat->skin->bgm : NULL); 60.1023 + } 60.1024 + if (!wantsClose) { 60.1025 + playSampleForTetromino(v->field->curPiece[1]); 60.1026 + restPollingMusic(24, bgmReadyGo ? v->plat->skin->bgm : NULL); 60.1027 + } 60.1028 +} 60.1029 + 60.1030 + 60.1031 +static void gameOverAnimation(const LJPCView *const v, const LJField *p, int won) { 60.1032 + int ceiling = p->ceiling; 60.1033 + int left = v->baseX + p->leftWall * v->skin->blkW; 60.1034 + int right = v->baseX + p->rightWall * v->skin->blkW - 1; 60.1035 + 60.1036 + ezPlaySample("sectionup_wav", 0); 60.1037 + if (!won) { 60.1038 + ezPlaySample("gameover_wav", 256); 60.1039 + } else { 60.1040 + ezPlaySample("win_wav", 256); 60.1041 + } 60.1042 + 60.1043 + for (int t = ceiling + v->skin->blkH - 2; t >= 0; --t) { 60.1044 + 60.1045 + // FIXME: vsync doesn't work on Vista 60.1046 + vsync(); 60.1047 + for (int row = ceiling - 1; row >= 0; --row) { 60.1048 + int ysub = t - row; 60.1049 + 60.1050 + if (ysub >= 0 && ysub < v->skin->blkH) { 60.1051 + int y = v->skin->baseY - v->skin->pfElev 60.1052 + - row * v->skin->blkH - ysub - 1; 60.1053 + hline(screen, left, y, right, pfBgColor); 60.1054 + } 60.1055 + } 60.1056 + if (wantsClose) { 60.1057 + t = 0; 60.1058 + } 60.1059 + } 60.1060 +} 60.1061 + 60.1062 +#define MENU_COPR_NOTICE_LINES 4 60.1063 +const char *const menuCoprNotice[MENU_COPR_NOTICE_LINES] = { 60.1064 + "Copr. 2006-2008 Damian Yerrick", 60.1065 + "Not sponsored or endorsed by The Tetris Company.", 60.1066 + "LOCKJAW comes with ABSOLUTELY NO WARRANTY. This is free software, and you are", 60.1067 + "welcome to redistribute it under certain conditions as described in GPL.txt." 60.1068 +}; 60.1069 + 60.1070 +static BITMAP *buildTitleScreen(void) { 60.1071 + BITMAP *back = create_system_bitmap(SCREEN_W, SCREEN_H); 60.1072 + if (!back) { 60.1073 + return NULL; 60.1074 + } 60.1075 + 60.1076 + // Gradient from (0, 0, 0) to (0, 0, 153) 60.1077 + for (int y = 0; y < 192; ++y) { 60.1078 + for (int x = -((y * 13) & 0x1F); 60.1079 + x < SCREEN_W; 60.1080 + x += 32) { 60.1081 + int colValue = y + ((rand() & 0x7000) >> 12); 60.1082 + int c = makecol(0, 0, 153 * colValue / 192); 60.1083 + hline(back, x, y, x + 31, c); 60.1084 + } 60.1085 + } 60.1086 + 60.1087 + // Gradient from (102, 51, 0) to (204, 102, 0) 60.1088 + for (int y = 192; y < 384; ++y) { 60.1089 + for (int x = -((y * 13) & 0x1F); 60.1090 + x < SCREEN_W; 60.1091 + x += 32) { 60.1092 + int colValue = y + ((rand() & 0x7800) >> 11); 60.1093 + int c = makecol(102 * colValue / 192, 51 * colValue / 192, 0); 60.1094 + hline(back, x, y, x + 31, c); 60.1095 + } 60.1096 + } 60.1097 + 60.1098 + // Gradient from (204, 102, 0) to (255, 128, 0) 60.1099 + for (int y = 384; y < SCREEN_H; ++y) { 60.1100 + for (int x = -((y * 13) & 0x1F); 60.1101 + x < SCREEN_W; 60.1102 + x += 32) { 60.1103 + int colValue = y - 400 + ((rand() & 0x7C00) >> 10); 60.1104 + if (colValue > 600 - 384) { 60.1105 + colValue = 600 - 384; 60.1106 + } 60.1107 + int c = makecol(204 + 50 * colValue / (600 - 384), 60.1108 + 102 + 25 * colValue / (600 - 384), 60.1109 + 0); 60.1110 + hline(back, x, y, x + 31, c); 60.1111 + } 60.1112 + } 60.1113 + 60.1114 + DATAFILE *obj = find_datafile_object(dat, "arttitle_bmp"); 60.1115 + BITMAP *logo = obj ? obj->dat : NULL; 60.1116 + obj = find_datafile_object(dat, "arttitle_pal"); 60.1117 + const RGB *pal = obj ? obj->dat : NULL; 60.1118 + 60.1119 + if (logo && pal) { 60.1120 + set_palette(pal); 60.1121 + draw_sprite(back, logo, 60.1122 + (SCREEN_W - logo->w) / 2, (384 - logo->h) / 2); 60.1123 + //unselect_palette(); 60.1124 + } 60.1125 + 60.1126 + textout_centre_ex(back, aver32, "Arrows: change; Enter: choose", 60.1127 + SCREEN_W / 2, 440, 60.1128 + 0, -1); 60.1129 + 60.1130 + textout_centre_ex(back, aver32, "LOCKJAW: The Reference "LJ_VERSION, 60.1131 + SCREEN_W / 2, SCREEN_H - 40, 60.1132 + 0, -1); 60.1133 + 60.1134 + return back; 60.1135 +} 60.1136 + 60.1137 +enum { 60.1138 + TITLE_EXIT = 0, 60.1139 + TITLE_PLAY, 60.1140 + TITLE_REPLAY, 60.1141 + TITLE_OPTIONS, 60.1142 + TITLE_SKIN, 60.1143 + TITLE_KEYS, 60.1144 + N_TITLE_ACTIONS 60.1145 +}; 60.1146 + 60.1147 +static const char *titleActions[N_TITLE_ACTIONS] = { 60.1148 + [TITLE_EXIT] = "Exit", 60.1149 + [TITLE_PLAY] = "Play", 60.1150 + [TITLE_REPLAY] = "Replay", 60.1151 + [TITLE_SKIN] = "Skin...", 60.1152 + [TITLE_OPTIONS] = "Options...", 60.1153 + [TITLE_KEYS] = "Game Keys..." 60.1154 +}; 60.1155 +/* 60.1156 + 0: Exit 60.1157 + 1: Play 60.1158 + 2 60.1159 +*/ 60.1160 +int title(void) { 60.1161 + 60.1162 + // don't even draw if the player is trying to close the window 60.1163 + if (wantsClose) { 60.1164 + return 0; 60.1165 + } 60.1166 + 60.1167 + BITMAP *back = buildTitleScreen(); 60.1168 + LJBits lastKeys = ~0; 60.1169 + int redraw = 1; 60.1170 + int choice = 1; 60.1171 + 60.1172 + if (!back) { 60.1173 + alert("Not enough memory to display the title screen.", 60.1174 + "(If you don't even have RAM for a title screen,", 60.1175 + "then what do you have RAM for?)", 60.1176 + "Exit", 0, 13, 0); 60.1177 + return 0; 60.1178 + } 60.1179 + 60.1180 + redrawWholeScreen = 1; 60.1181 + 60.1182 + for(int done = 0; done == 0; ) { 60.1183 + if (redrawWholeScreen) { 60.1184 + redrawWholeScreen = 0; 60.1185 + blit(back, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H); 60.1186 + redraw = 1; 60.1187 + } 60.1188 + if (redraw) { 60.1189 + int dehilite = makecol(221, 153, 85); 60.1190 + int white = makecol(255, 255, 255); 60.1191 + redraw = 0; 60.1192 + vsync(); 60.1193 + blit(back, screen, 0, 400, 0, 400, SCREEN_W, 30); 60.1194 + textout_centre_ex(screen, aver32, 60.1195 + titleActions[choice], 60.1196 + SCREEN_W / 2, 400, white, -1); 60.1197 + textout_centre_ex(screen, aver32, 60.1198 + titleActions[choice == 0 ? N_TITLE_ACTIONS - 1 : choice - 1], 60.1199 + SCREEN_W / 2 - 160, 400, dehilite, -1); 60.1200 + textout_centre_ex(screen, aver32, 60.1201 + titleActions[choice == N_TITLE_ACTIONS - 1 ? 0 : choice + 1], 60.1202 + SCREEN_W / 2 + 160, 400, dehilite, -1); 60.1203 + } 60.1204 + 60.1205 + while (keypressed()) { 60.1206 + int scancode; 60.1207 + ureadkey(&scancode); 60.1208 + if (scancode == KEY_PRTSCR) { 60.1209 + saveScreen(-1); 60.1210 + } 60.1211 + } 60.1212 + 60.1213 + LJBits keys = menuReadPad(); 60.1214 + LJBits newKeys = keys & ~lastKeys; 60.1215 + 60.1216 + if (newKeys & VKEY_LEFT) { 60.1217 + --choice; 60.1218 + redraw = 1; 60.1219 + ezPlaySample("shift_wav", 128); 60.1220 + } 60.1221 + if (newKeys & VKEY_RIGHT) { 60.1222 + ++choice; 60.1223 + redraw = 1; 60.1224 + ezPlaySample("shift_wav", 128); 60.1225 + } 60.1226 + if (newKeys & VKEY_ROTL) { 60.1227 + choice = 0; 60.1228 + redraw = 1; 60.1229 + ezPlaySample("rotate_wav", 128); 60.1230 + } 60.1231 + if (newKeys & VKEY_ROTR) { 60.1232 + done = 1; 60.1233 + ezPlaySample("line_wav", 128); 60.1234 + } 60.1235 + if (choice < 0) { 60.1236 + choice += N_TITLE_ACTIONS; 60.1237 + } 60.1238 + if (choice >= N_TITLE_ACTIONS) { 60.1239 + choice -= N_TITLE_ACTIONS; 60.1240 + } 60.1241 + 60.1242 + lastKeys = keys; 60.1243 + 60.1244 + if (!redraw) { 60.1245 + rest(30); 60.1246 + } 60.1247 + if (wantsClose) { 60.1248 + done = 1; 60.1249 + choice = 0; 60.1250 + } 60.1251 + } 60.1252 + destroy_bitmap(back); 60.1253 + return choice; 60.1254 +} 60.1255 + 60.1256 + 60.1257 +void setupWindow(void) { 60.1258 + set_window_title("LOCKJAW"); 60.1259 + bgColor = makecol(255, 255, 255); 60.1260 + pfFgColor = bgColor; 60.1261 + hiliteColor = makecol(255, 255, 204); 60.1262 + refreshRate = get_refresh_rate(); 60.1263 +} 60.1264 + 60.1265 +void drawCoprNotice(void) { 60.1266 + clear_to_color(screen, pfBgColor); 60.1267 + for (int i = 0; i < MENU_COPR_NOTICE_LINES; ++i) { 60.1268 + textout_ex(screen, font, menuCoprNotice[i], 60.1269 + 16, 580 + 12 * (i - MENU_COPR_NOTICE_LINES), 60.1270 + pfFgColor, -1); 60.1271 + } 60.1272 +} 60.1273 + 60.1274 + 60.1275 +/** 60.1276 + * Destroys the back buffers for both playfields. 60.1277 + */ 60.1278 +static void destroyBackBuf(LJPCView *plat) { 60.1279 + for (int i = 0; i < MAX_PLAYERS; ++i) { 60.1280 + if (plat->back) { 60.1281 + destroy_bitmap(plat->back); 60.1282 + plat->back = NULL; 60.1283 + } 60.1284 + } 60.1285 + if (plat->skin->fullback) { 60.1286 + destroy_bitmap(plat->skin->fullback); 60.1287 + plat->skin->fullback = NULL; 60.1288 + } 60.1289 +} 60.1290 + 60.1291 +/** 60.1292 + * Creates the back buffers for both playfields. 60.1293 + */ 60.1294 +static int createBackBuf(LJView *v) { 60.1295 + v->plat->skin->fullback = create_system_bitmap(SCREEN_W, SCREEN_H); 60.1296 + if(!v->plat->skin->fullback) { 60.1297 + allegro_message("Could not create back buffer.\n"); 60.1298 + return 0; 60.1299 + } 60.1300 + 60.1301 + int blkW = v->plat->skin->blkW; 60.1302 + int blkH = v->plat->skin->blkH; 60.1303 + int y = v->plat->skin->baseY 60.1304 + - v->plat->skin->pfElev 60.1305 + - blkH * v->field->ceiling; 60.1306 + 60.1307 + for (int i = 0; i < MAX_PLAYERS; ++i) { 60.1308 + int x = v->plat->skin->baseX 60.1309 + + blkW * v[i].field->leftWall; 60.1310 + 60.1311 + v[i].plat->back = create_sub_bitmap(v->plat->skin->fullback, 60.1312 + x + SCREEN_W / 2 * i, 60.1313 + y, 60.1314 + v->plat->skin->blkW * LJ_PF_WID, 60.1315 + v->plat->skin->blkH * LJ_PF_VIS_HT); 60.1316 + if (!v[i].plat->back) { 60.1317 + destroyBackBuf(v->plat); 60.1318 + return 0; 60.1319 + } 60.1320 + } 60.1321 + return 1; 60.1322 +} 60.1323 + 60.1324 +/** 60.1325 + * Destroys all system bitmaps that a given view owns. 60.1326 + * Useful before changing screen mode. 60.1327 + */ 60.1328 +void destroySystemBitmaps(LJPCView *plat) { 60.1329 + destroyBackBuf(plat); 60.1330 + if (plat->skin->connBlocks) { 60.1331 + destroy_bitmap(plat->skin->connBlocks); 60.1332 + plat->skin->connBlocks = NULL; 60.1333 + } 60.1334 +} 60.1335 + 60.1336 +int openWindow(int windowed) 60.1337 +{ 60.1338 + int depth = desktop_color_depth(); 60.1339 + int card = windowed ? GFX_AUTODETECT_WINDOWED : GFX_AUTODETECT_FULLSCREEN; 60.1340 + 60.1341 + /* Reference implementation for Allegro is not compatible with 60.1342 + indexed color. */ 60.1343 + if (depth < 15) { 60.1344 + depth = 16; 60.1345 + } 60.1346 + 60.1347 + // Full screen procedure 60.1348 + set_color_depth(depth); 60.1349 + if (set_gfx_mode(card, skinW, skinH, 0, 0) == 0) { 60.1350 + setupWindow(); 60.1351 + return 0; 60.1352 + } 60.1353 + 60.1354 + // Windows can't tell 16 bit from 15 bit. If desktop color depth is reported as 16, try 15 too. 60.1355 + if (depth == 16) { 60.1356 + set_color_depth(15); 60.1357 + if (set_gfx_mode(card, skinW, skinH, 0, 0) == 0) { 60.1358 + setupWindow(); 60.1359 + return 0; 60.1360 + } 60.1361 + } 60.1362 + 60.1363 + return -1; 60.1364 +} 60.1365 + 60.1366 +BITMAP *loadConnections(const char *filename, int blkW, int blkH) { 60.1367 + BITMAP *src = load_bitmap(filename, NULL); 60.1368 + 60.1369 + if (!src) { 60.1370 + return NULL; 60.1371 + } 60.1372 + BITMAP *dst = create_system_bitmap(blkW*16, blkH*16); 60.1373 + if (!dst) { 60.1374 + destroy_bitmap(src); 60.1375 + return NULL; 60.1376 + } 60.1377 + acquire_bitmap(dst); 60.1378 + for (unsigned int col = 0; col < 16; ++col) { 60.1379 + unsigned int srcXBase = (col & 0x03) * blkW * 2; 60.1380 + unsigned int srcYBase = (col >> 2) * blkH * 2; 60.1381 + unsigned int dstYBase = col * blkH; 60.1382 + for (unsigned int conn = 0; conn < 16; ++conn) { 60.1383 + unsigned int dstXBase = conn * blkW; 60.1384 + unsigned int topSegY = (conn & CONNECT_U) ? blkH : 0; 60.1385 + unsigned int botSegY = (conn & CONNECT_D) ? blkH/2 : 3*blkH/2; 60.1386 + unsigned int leftSegX = (conn & CONNECT_L) ? blkW : 0; 60.1387 + unsigned int rightSegX = (conn & CONNECT_R) ? blkW/2 : 3*blkW/2; 60.1388 + blit(src, dst, 60.1389 + srcXBase + leftSegX, srcYBase + topSegY, 60.1390 + dstXBase + 0, dstYBase + 0, 60.1391 + blkW/2, blkH/2); 60.1392 + blit(src, dst, 60.1393 + srcXBase + rightSegX, srcYBase + topSegY, 60.1394 + dstXBase + blkW/2, dstYBase + 0, 60.1395 + blkW/2, blkH/2); 60.1396 + blit(src, dst, 60.1397 + srcXBase + leftSegX, srcYBase + botSegY, 60.1398 + dstXBase + 0, dstYBase + blkH/2, 60.1399 + blkW/2, blkH/2); 60.1400 + blit(src, dst, 60.1401 + srcXBase + rightSegX, srcYBase + botSegY, 60.1402 + dstXBase + blkW/2, dstYBase + blkH/2, 60.1403 + blkW/2, blkH/2); 60.1404 + } 60.1405 + } 60.1406 + release_bitmap(dst); 60.1407 + destroy_bitmap(src); 60.1408 + return dst; 60.1409 +} 60.1410 + 60.1411 +void closeWindow(void) { 60.1412 + set_gfx_mode(GFX_TEXT, 0, 0, 0, 0); 60.1413 +} 60.1414 + 60.1415 +static void mainCleanup(LJPCView *v) { 60.1416 + destroyBackBuf(v); 60.1417 + if (v->skin->blocks) { 60.1418 + destroy_bitmap(v->skin->blocks); 60.1419 + } 60.1420 + if (v->skin->connBlocks) { 60.1421 + destroy_bitmap(v->skin->connBlocks); 60.1422 + } 60.1423 + LJMusic_delete(v->skin->bgm); 60.1424 + if (withSound) { 60.1425 + remove_sound(); 60.1426 + } 60.1427 + closeWindow(); 60.1428 +} 60.1429 + 60.1430 +/** 60.1431 + * Resets all skin settings to their initial values 60.1432 + * so that skins can override them. 60.1433 + */ 60.1434 +void loadSkinDefaults(LJPCSkin *s) { 60.1435 + ustrzcpy(ljblocksSRSName, sizeof(ljblocksSRSName) - 1, 60.1436 + "ljblocks.bmp"); 60.1437 + ustrzcpy(ljblocksSegaName, sizeof(ljblocksSegaName) - 1, 60.1438 + "ljblocks-sega.bmp"); 60.1439 + ustrzcpy(ljconnSRSName, sizeof(ljconnSRSName) - 1, 60.1440 + "ljconn.bmp"); 60.1441 + ustrzcpy(ljconnSegaName, sizeof(ljconnSegaName) - 1, 60.1442 + "ljconn-sega.bmp"); 60.1443 + ustrzcpy(ljbgName, sizeof(ljbgName) - 1, 60.1444 + "ljbg.jpg"); 60.1445 + ustrzcpy(menubgName, sizeof(ljbgName) - 1, 60.1446 + "menubg.jpg"); 60.1447 + ustrzcpy(bgmName, sizeof(bgmName) - 1, 60.1448 + "bgm.s3m"); 60.1449 + ustrzcpy(bgmRhythmName, sizeof(bgmRhythmName) - 1, 60.1450 + "bgm-rhythm.s3m"); 60.1451 + bgmLoopPoint = 0; 60.1452 + bgmReadyGo = 0; 60.1453 + bgmVolume = 128; 60.1454 + bgColor = makecol(255, 255, 255); 60.1455 + fgColor = makecol(0, 0, 0); 60.1456 + hiliteColor = makecol(255, 255, 204); 60.1457 + pfBgColor = makecol(0, 0, 0); 60.1458 + pfFgColor = makecol(255, 255, 255); 60.1459 + s->blkW = 24; 60.1460 + s->blkH = 24; 60.1461 + s->transparentPF = 0; 60.1462 + s->shiftScale = 0; 60.1463 + s->baseX = 0; 60.1464 + s->nextPos = 0; 60.1465 +} 60.1466 + 60.1467 +/** 60.1468 + * Converts a single hexadecimal digit to its value. 60.1469 + * @param in USASCII/Unicode value of hex digit character 60.1470 + * @return value 60.1471 +*/ 60.1472 +int hexDigitValue(int in) { 60.1473 + if (in >= '0' && in <= '9') { 60.1474 + return in - '0'; 60.1475 + } else if (in >= 'A' && in <= 'F') { 60.1476 + return in - 'A' + 10; 60.1477 + } else if (in >= 'a' && in <= 'f') { 60.1478 + return in - 'a' + 10; 60.1479 + } else { 60.1480 + return -1; 60.1481 + } 60.1482 +} 60.1483 + 60.1484 +int translateComponent(const char **in, size_t nDigits) { 60.1485 + const char *here = *in; 60.1486 + int hi = hexDigitValue(*here++); 60.1487 + int lo = nDigits > 3 ? hexDigitValue(*here++) : hi; 60.1488 + *in = here; 60.1489 + if (hi >= 0 && lo >= 0) { 60.1490 + return hi * 16 + lo; 60.1491 + } else { 60.1492 + return -1; 60.1493 + } 60.1494 +} 60.1495 + 60.1496 +/** 60.1497 + * Interprets a hexadecimal color specifier. 60.1498 + * @param in hexadecimal color specifier, in #ABC or #AABBCC format 60.1499 + * @return Allegro device-dependent color, in makecol() format 60.1500 + */ 60.1501 +int translateColor(const char *in) { 60.1502 + size_t nDigits = 0; 60.1503 + 60.1504 + // Verify and skip # 60.1505 + if (*in != '#') { 60.1506 + return -1; 60.1507 + } 60.1508 + ++in; 60.1509 + 60.1510 + // Determine whether we have a 3-digit color or a 6-digit color 60.1511 + for (const char *here = in; 60.1512 + hexDigitValue(*here) >= 0; 60.1513 + ++here) { 60.1514 + ++nDigits; 60.1515 + } 60.1516 + if (nDigits != 3 && nDigits != 6) { 60.1517 + return -1; 60.1518 + } 60.1519 + 60.1520 + int red = translateComponent(&in, nDigits); 60.1521 + int green = translateComponent(&in, nDigits); 60.1522 + int blue = translateComponent(&in, nDigits); 60.1523 + if (red >= 0 && green >= 0 && blue >= 0) { 60.1524 + return makecol(red, green, blue); 60.1525 + } else { 60.1526 + return -1; 60.1527 + } 60.1528 +}; 60.1529 + 60.1530 +/** 60.1531 + * Loads skin parameters from the file. 60.1532 + * @param skinName Name of skin ini file 60.1533 + */ 60.1534 +int loadSkinFile(LJPCSkin *s, const char *skinName) { 60.1535 + 60.1536 + // Don't use ljfopen here because lj.ini specifies the absolute skin file 60.1537 + FILE *fp = fopen(skinName, "rt"); 60.1538 + char key[1024], var[1024], val[1024], input_buf[1024]; 60.1539 + 60.1540 + key[0] = 0; 60.1541 + var[0] = 0; 60.1542 + val[0] = 0; 60.1543 + loadSkinDefaults(s); 60.1544 + 60.1545 + if (!fp) return 0; 60.1546 + while(1) { 60.1547 + int rval; 60.1548 + 60.1549 + if(!fgets (input_buf, sizeof(input_buf), fp)) 60.1550 + break; 60.1551 + rval = parse_ini_line(input_buf, key, var, val); 60.1552 + 60.1553 + if(!ustrcmp ("ljblocksSRS", var)) { 60.1554 + ustrzcpy(ljblocksSRSName, sizeof(ljblocksSRSName) - 1, val); 60.1555 + } 60.1556 + else if(!ustrcmp ("ljblocksSega", var)) { 60.1557 + ustrzcpy(ljblocksSegaName, sizeof(ljblocksSegaName) - 1, val); 60.1558 + } 60.1559 + else if(!ustrcmp ("ljconnSRS", var)) { 60.1560 + ustrzcpy(ljconnSRSName, sizeof(ljconnSRSName) - 1, val); 60.1561 + } 60.1562 + else if(!ustrcmp ("ljconnSega", var)) { 60.1563 + ustrzcpy(ljconnSegaName, sizeof(ljconnSegaName) - 1, val); 60.1564 + } 60.1565 + else if(!ustrcmp ("bgm", var)) { 60.1566 + ustrzcpy(bgmName, sizeof(bgmName) - 1, val); 60.1567 + } 60.1568 + else if(!ustrcmp ("bgmRhythm", var)) { 60.1569 + ustrzcpy(bgmRhythmName, sizeof(bgmRhythmName) - 1, val); 60.1570 + } 60.1571 + else if(!ustrcmp ("ljbg", var)) { 60.1572 + ustrzcpy(ljbgName, sizeof(ljbgName) - 1, val); 60.1573 + } 60.1574 + else if(!ustrcmp ("bgmLoopPoint", var)) { 60.1575 + unsigned long int c = strtoul(val, NULL, 0); 60.1576 + if (c >= 0) { 60.1577 + bgmLoopPoint = c; 60.1578 + } 60.1579 + } 60.1580 + else if(!ustrcmp ("bgmVolume", var)) { 60.1581 + unsigned long int c = strtol(val, NULL, 0); 60.1582 + if (c >= 0) { 60.1583 + bgmVolume = c; 60.1584 + } 60.1585 + } 60.1586 + else if(!ustrcmp ("bgmReadyGo", var)) { 60.1587 + unsigned long int c = strtoul(val, NULL, 0); 60.1588 + if (c >= 0) { 60.1589 + bgmReadyGo = c; 60.1590 + } 60.1591 + } 60.1592 + else if(!ustrcmp ("pfbgcolor", var)) { 60.1593 + int c = translateColor(val); 60.1594 + if (c >= 0) { 60.1595 + pfBgColor = c; 60.1596 + } 60.1597 + } 60.1598 + else if(!ustrcmp ("pfcolor", var)) { 60.1599 + int c = translateColor(val); 60.1600 + if (c >= 0) { 60.1601 + pfFgColor = c; 60.1602 + } 60.1603 + } 60.1604 + else if(!ustrcmp ("bgcolor", var)) { 60.1605 + int c = translateColor(val); 60.1606 + if (c >= 0) { 60.1607 + bgColor = c; 60.1608 + } 60.1609 + } 60.1610 + else if(!ustrcmp ("color", var)) { 60.1611 + int c = translateColor(val); 60.1612 + if (c >= 0) { 60.1613 + fgColor = c; 60.1614 + } 60.1615 + } 60.1616 + else if(!ustrcmp ("hilitecolor", var)) { 60.1617 + int c = translateColor(val); 60.1618 + if (c >= 0) { 60.1619 + hiliteColor = c; 60.1620 + } 60.1621 + } 60.1622 + else if(!ustrcmp ("blkW", var)) { 60.1623 + int c = strtol(val, NULL, 0); 60.1624 + if (c >= 0) { 60.1625 + s->blkW = c; 60.1626 + } 60.1627 + } 60.1628 + else if(!ustrcmp ("blkH", var)) { 60.1629 + int c = strtol(val, NULL, 0); 60.1630 + if (c >= 0) { 60.1631 + s->blkH = c; 60.1632 + } 60.1633 + } 60.1634 + else if(!ustrcmp ("transparentPF", var)) { 60.1635 + int c = atoi(val); 60.1636 + if (c >= 0) { 60.1637 + s->transparentPF = c; 60.1638 + } 60.1639 + } 60.1640 + else if(!ustrcmp ("shiftScale", var)) { 60.1641 + int c = atoi(val); 60.1642 + if (c >= 0) { 60.1643 + s->shiftScale = c; 60.1644 + } 60.1645 + } 60.1646 + else if(!ustrcmp ("baseX", var)) { 60.1647 + int c = atoi(val); 60.1648 + if (c >= 0) { 60.1649 + s->baseX = c; 60.1650 + } 60.1651 + } 60.1652 + else if(!ustrcmp ("nextPos", var)) { 60.1653 + int c = atoi(val); 60.1654 + if (c >= 0) { 60.1655 + s->nextPos = c; 60.1656 + } 60.1657 + } 60.1658 + else if(!ustrcmp ("wndW", var)) { 60.1659 + unsigned long int c = strtol(val, NULL, 0); 60.1660 + if (c >= 0) { 60.1661 + skinW = c; 60.1662 + } 60.1663 + } 60.1664 + else if(!ustrcmp ("wndH", var)) { 60.1665 + unsigned long int c = strtoul(val, NULL, 0); 60.1666 + if (c >= 0) { 60.1667 + skinH = c; 60.1668 + } 60.1669 + } 60.1670 + 60.1671 + } 60.1672 + fclose(fp); 60.1673 + ljpathSetSkinFolder(skinName); 60.1674 + return 0; 60.1675 +} 60.1676 + 60.1677 +static void drawProgressSegment(int min, int max) { 60.1678 + min = min * SCREEN_W / 100; 60.1679 + max = max * SCREEN_W / 100; 60.1680 + int blue = makecol(0, 0, 255); 60.1681 + rectfill(screen, min, SCREEN_H - 8, max - 1, SCREEN_H - 5, blue); 60.1682 + int orange = makecol(255, 128, 0); 60.1683 + rectfill(screen, min, SCREEN_H - 4, max - 1, SCREEN_H - 1, orange); 60.1684 +} 60.1685 + 60.1686 +static int loadSkin(LJView *v, const char *skinName) { 60.1687 + BITMAP *bmp; 60.1688 + const LJRotSystem *rs = rotSystems[v->field->rotationSystem]; 60.1689 + int colorScheme = rs->colorScheme; 60.1690 + 60.1691 + rectfill(screen, 0, 592, 799, 599, 0); 60.1692 + loadSkinFile(v->plat->skin, skinName); 60.1693 + 60.1694 + destroyBackBuf(v->plat); 60.1695 + if (!createBackBuf(v)) { 60.1696 + allegro_message("Could not create back buffer.\n"); 60.1697 + return 1; 60.1698 + } 60.1699 + 60.1700 + drawProgressSegment(0, 20); 60.1701 + 60.1702 + // Load background image 60.1703 + char path[PATH_MAX]; 60.1704 + bmp = ljpathFind_r(path, ljbgName) 60.1705 + ? load_bitmap(path, NULL) : NULL; 60.1706 + if (v->plat->skin->bg) { 60.1707 + destroy_bitmap(v->plat->skin->bg); 60.1708 + } 60.1709 + if (bmp) { 60.1710 + 60.1711 + // If the image size doesn't match the window size, resize it 60.1712 + if (bmp->w != SCREEN_W || bmp->h != SCREEN_H) { 60.1713 + BITMAP *resized = create_bitmap(SCREEN_W, SCREEN_H); 60.1714 + 60.1715 + if (resized) { 60.1716 + stretch_blit(bmp, resized, 0, 0, bmp->w, bmp->h, 60.1717 + 0, 0, SCREEN_W, SCREEN_H); 60.1718 + destroy_bitmap(bmp); 60.1719 + } 60.1720 + if (bmp) { 60.1721 + bmp = resized; 60.1722 + } else { 60.1723 + allegro_message("Background image \"%s\" resize failed.\n", 60.1724 + ljbgName); 60.1725 + } 60.1726 + } 60.1727 + } 60.1728 + if(!bmp) { 60.1729 + bmp = create_bitmap(SCREEN_W, SCREEN_H); 60.1730 + if (bmp) { 60.1731 + allegro_message("Background image \"%s\" not found.\n" 60.1732 + "Using plain background instead.\n", 60.1733 + ljbgName); 60.1734 + clear_to_color(bmp, bgColor); 60.1735 + } else { 60.1736 + allegro_message("Background image \"%s\" not found.\n", 60.1737 + ljbgName); 60.1738 + return 0; 60.1739 + } 60.1740 + } 60.1741 + v->plat->skin->bg = bmp; 60.1742 + drawProgressSegment(20, 40); 60.1743 + 60.1744 + // load block images 60.1745 + if (v->plat->skin->blocks) { 60.1746 + destroy_bitmap(v->plat->skin->blocks); 60.1747 + } 60.1748 + bmp = ljpathFind_r(path, colorScheme 60.1749 + ? ljblocksSegaName 60.1750 + : ljblocksSRSName) 60.1751 + ? load_bitmap(path, NULL) : NULL; 60.1752 + v->plat->skin->blocks = bmp; 60.1753 + if(!v->plat->skin->blocks) { 60.1754 + allegro_message("Background image \"%s\" not found.\n", 60.1755 + ljbgName); 60.1756 + return 0; 60.1757 + } 60.1758 + drawProgressSegment(40, 60); 60.1759 + 60.1760 + // load connected block images 60.1761 + if (v->plat->skin->connBlocks) { 60.1762 + destroy_bitmap(v->plat->skin->connBlocks); 60.1763 + } 60.1764 + bmp = ljpathFind_r(path, colorScheme 60.1765 + ? ljconnSegaName 60.1766 + : ljconnSRSName) 60.1767 + ? loadConnections(path, 60.1768 + v->plat->skin->blkW, 60.1769 + v->plat->skin->blkH) 60.1770 + : NULL; 60.1771 + v->plat->skin->connBlocks = bmp; 60.1772 + drawProgressSegment(60, 80); 60.1773 + 60.1774 + // load music 60.1775 + int isRhythm = (v->field->speedState.curve == LJSPD_RHYTHM); 60.1776 + if (ljpathFind_r(path, isRhythm ? bgmRhythmName : bgmName)) { 60.1777 + LJMusic_load(v->plat->skin->bgm, path); 60.1778 + } 60.1779 + if (!isRhythm) { 60.1780 + LJMusic_setLoop(v->plat->skin->bgm, bgmLoopPoint); 60.1781 + } 60.1782 + drawProgressSegment(80, 100); 60.1783 + return 1; 60.1784 +} 60.1785 + 60.1786 +void drop_mouse(void) { 60.1787 + while (mouse_y < SCREEN_H - 25) { 60.1788 + position_mouse(mouse_x, mouse_y + 20); 60.1789 + rest(10); 60.1790 + } 60.1791 + ezPlaySample("land_wav", 192); 60.1792 + position_mouse(mouse_x, SCREEN_H - 5); 60.1793 + ezPlaySample("lock_wav", 192); 60.1794 +} 60.1795 + 60.1796 +int pickReplay(void) { 60.1797 + FONT *oldFont = font; 60.1798 + font = (FONT *)aver16; 60.1799 + install_mouse(); 60.1800 + int got = file_select_ex("Choose a demo:", demoFilename, "ljm", sizeof(demoFilename), 600, 400); 60.1801 + drop_mouse(); 60.1802 + remove_mouse(); 60.1803 + font = oldFont; 60.1804 + return got ? 0 : -1; 60.1805 +} 60.1806 + 60.1807 +void badReplay(void) { 60.1808 + acquire_screen(); 60.1809 + clear_to_color(screen, bgColor); 60.1810 + textout_ex(screen, aver32, "The demo", 100, 100, fgColor, -1); 60.1811 + textout_ex(screen, aver16, demoFilename, 100, 130, fgColor, -1); 60.1812 + textout_ex(screen, aver32, "could not be played because it was", 100, 150, fgColor, -1); 60.1813 + textout_ex(screen, aver32, "recorded with a different version", 100, 180, fgColor, -1); 60.1814 + textout_ex(screen, aver32, "of LOCKJAW software.", 100, 210, fgColor, -1); 60.1815 + release_screen(); 60.1816 + 60.1817 + LJBits lastKeys = ~0; 60.1818 + LJBits keys, newKeys = 0; 60.1819 + 60.1820 + do { 60.1821 + keys = menuReadPad(); 60.1822 + newKeys = keys & ~lastKeys; 60.1823 + lastKeys = keys; 60.1824 + rest(30); 60.1825 + } while (!(newKeys & (VKEY_ROTL | VKEY_ROTR))); 60.1826 +} 60.1827 + 60.1828 +int pickSkin(void) { 60.1829 + FONT *oldFont = font; 60.1830 + font = (FONT *)aver16; 60.1831 + install_mouse(); 60.1832 + int got = file_select_ex("Choose a skin:", skinName, "skin", sizeof(skinName), 600, 400); 60.1833 + drop_mouse(); 60.1834 + remove_mouse(); 60.1835 + font = oldFont; 60.1836 + return got ? 0 : -1; 60.1837 +} 60.1838 + 60.1839 +void calcElev(LJView *v) { 60.1840 + int blkH = v->plat->skin->blkH; 60.1841 + int ceiling = v->field->ceiling; 60.1842 + int elev = (LJ_PF_VIS_HT - ceiling) * blkH; 60.1843 + 60.1844 + if (elev > 480 - ceiling * blkH) { 60.1845 + elev = 480 - ceiling * blkH; 60.1846 + } 60.1847 + if (elev < 0) { 60.1848 + elev = 0; 60.1849 + } 60.1850 + v->plat->skin->pfElev = elev; 60.1851 +} 60.1852 + 60.1853 +int main(const int argc, const char *const *argv) { 60.1854 + const char *cmdLineDemo = NULL; 60.1855 + int lastPreset = -2; // start out with full custom 60.1856 + LJPCSkin skin = { 60.1857 + .baseY = 552, 60.1858 + .blkW = 24, 60.1859 + .blkH = 24 60.1860 + }; 60.1861 + LJField p[MAX_PLAYERS]; 60.1862 + LJControl control[2] = { 60.1863 + { 60.1864 + .replaySrc = 0, 60.1865 + .replayDst = 0 60.1866 + } 60.1867 + }; 60.1868 + LJPCView platView[MAX_PLAYERS] = { 60.1869 + { 60.1870 + .skin = &skin 60.1871 + }, 60.1872 + { 60.1873 + .skin = &skin 60.1874 + } 60.1875 + }; 60.1876 + LJView mainView[MAX_PLAYERS] = { 60.1877 + { 60.1878 + .field = &p[0], 60.1879 + .control = &control[0], 60.1880 + .plat = &platView[0], 60.1881 + .backDirty = ~0 60.1882 + }, 60.1883 + { 60.1884 + .field = &p[1], 60.1885 + .control = &control[1], 60.1886 + .plat = &platView[1], 60.1887 + .backDirty = ~0, 60.1888 + } 60.1889 + }; 60.1890 + 60.1891 + struct LJPrefs prefs = { 60.1892 + .number = { 60.1893 + [OPTIONS_TRAILS] = 1, 60.1894 + [OPTIONS_AUTO_PAUSE] = 1, 60.1895 + [OPTIONS_AUTO_RECORD] = 0, 60.1896 + [OPTIONS_WINDOWED] = 1 60.1897 + } 60.1898 + }; 60.1899 + 60.1900 + // as of 0.46, we're starting to make it a bit more 2-player-clean 60.1901 + int nPlayers = 1; 60.1902 + 60.1903 + allegro_init(); 60.1904 + ljpathInit(argc > 0 ? argv[0] : "."); 60.1905 + install_timer(); 60.1906 + initOptions(prefs.number); 60.1907 + loadOptions(&prefs); 60.1908 + loadSkinFile(&skin, skinName); 60.1909 + 60.1910 + if (argc > 1) { 60.1911 + if (argv[1][0] == '-') { 60.1912 + if (!ustrcmp("--help", argv[1]) 60.1913 + || !ustrcmp("-h", argv[1])) { 60.1914 + allegro_message("Usage: lj [DEMOFILE]\n"); 60.1915 + return 0; 60.1916 + } 60.1917 + } else { 60.1918 + cmdLineDemo = argv[1]; 60.1919 + } 60.1920 + } 60.1921 + 60.1922 + if (openWindow(prefs.number[OPTIONS_WINDOWED]) != 0) { 60.1923 + allegro_message("LOCKJAW fatal error: Could not open an %dx%d pixel %s.\n" 60.1924 + "Trying %s next time.\n", 60.1925 + skinW, skinH, 60.1926 + prefs.number[OPTIONS_WINDOWED] ? "window": "screen mode", 60.1927 + prefs.number[OPTIONS_WINDOWED] ? "the full screen": "a window"); 60.1928 + prefs.number[OPTIONS_WINDOWED] = !prefs.number[OPTIONS_WINDOWED]; 60.1929 + saveOptions(&prefs); 60.1930 + return EXIT_FAILURE; 60.1931 + } 60.1932 + drawCoprNotice(); 60.1933 + LOCK_FUNCTION(incCurTime); 60.1934 + LOCK_VARIABLE(curTime); 60.1935 + install_int_ex(incCurTime, BPM_TO_TIMER(LJ_TICK_RATE)); 60.1936 + 60.1937 + jpgalleg_init(); 60.1938 + set_color_conversion(COLORCONV_NONE); 60.1939 + { 60.1940 + char path[PATH_MAX]; 60.1941 + if (ljpathFind_r(path, "lj.dat")) { 60.1942 + dat = load_datafile(path); 60.1943 + } 60.1944 + } 60.1945 + set_color_conversion(COLORCONV_TOTAL); 60.1946 + if(!dat) { 60.1947 + closeWindow(); 60.1948 + allegro_message("LOCKJAW fatal error: Could not load datafile lj.dat\n"); 60.1949 + return 1; 60.1950 + } 60.1951 + 60.1952 + { 60.1953 + const DATAFILE *aver16dat = find_datafile_object(dat, "Aver16_bmp"); 60.1954 + aver16 = aver16dat ? aver16dat->dat : font; 60.1955 + const DATAFILE *aver32dat = find_datafile_object(dat, "Aver32_bmp"); 60.1956 + aver32 = aver32dat ? aver32dat->dat : aver16; 60.1957 + } 60.1958 + 60.1959 + LOCK_FUNCTION(amnesia); 60.1960 + LOCK_VARIABLE(redrawWholeScreen); 60.1961 + 60.1962 + // If we can be notified on switching out, take this notification. 60.1963 + if (set_display_switch_mode(SWITCH_BACKGROUND) >= 0 60.1964 + || set_display_switch_mode(SWITCH_BACKAMNESIA) >= 0) { 60.1965 + set_display_switch_callback(SWITCH_OUT, requestPause); 60.1966 + } 60.1967 + set_display_switch_callback(SWITCH_IN, amnesia); 60.1968 + 60.1969 + install_keyboard(); 60.1970 + initKeys(); 60.1971 + 60.1972 + for (int i = 0; i < MAX_PLAYERS; ++i) { 60.1973 + p[i].seed = time(NULL) + 123456789*i; 60.1974 + } 60.1975 + 60.1976 + reserve_voices(8, 0); 60.1977 + set_volume_per_voice(0); 60.1978 +#ifdef ALLEGRO_WINDOWS 60.1979 + // Under Windows, use the Allegro mixer because on my machine 60.1980 + // and probably others, the built-in mixer will replace the very 60.1981 + // beginning of one sound with the end of the last sound played 60.1982 + // on that voice. 60.1983 + withSound = !install_sound(DIGI_DIRECTAMX(0), MIDI_NONE, NULL); 60.1984 +#else 60.1985 + withSound = !install_sound(DIGI_AUTODETECT, MIDI_NONE, NULL); 60.1986 +#endif 60.1987 + skin.bgm = LJMusic_new(); 60.1988 + { 60.1989 + char path[PATH_MAX]; 60.1990 + if (ljpathFind_r(path, "sound.dat")) { 60.1991 + sound_dat = load_datafile(path); 60.1992 + } 60.1993 + } 60.1994 + 60.1995 + for (int i = 0; i < MAX_PLAYERS; ++i) { 60.1996 + unpackOptions(&mainView[i], &prefs); 60.1997 + } 60.1998 + if (loadSkin(&mainView[0], skinName) < 0) { 60.1999 + mainCleanup(platView); 60.2000 + return EXIT_FAILURE; 60.2001 + } else { 60.2002 + 60.2003 + } 60.2004 + 60.2005 + if(!skin.blocks) { 60.2006 + mainCleanup(platView); 60.2007 + allegro_message("Blocks image \"%s\" not found.\n", 60.2008 + p[0].rotationSystem 60.2009 + ? ljblocksSegaName 60.2010 + : ljblocksSRSName); 60.2011 + return 1; 60.2012 + } 60.2013 + 60.2014 + srand(time(NULL)); 60.2015 + 60.2016 + // Wait for copyright notice to be displayed "conspicuously" 60.2017 + if (curTime < 180) { 60.2018 + rest(3100 - curTime * 16); 60.2019 + } 60.2020 + 60.2021 + for (int action = cmdLineDemo ? TITLE_REPLAY : title(); 60.2022 + action > TITLE_EXIT && !wantsClose; 60.2023 + action = title()) { 60.2024 + switch (action) { 60.2025 + case TITLE_PLAY: 60.2026 + for (int preset = getPreset(lastPreset); 60.2027 + preset != -1 && !wantsClose; 60.2028 + preset = getPreset(lastPreset)) 60.2029 + { 60.2030 + lastPreset = preset; 60.2031 + for (int i = 0; i < MAX_PLAYERS; ++i) { 60.2032 + unpackOptions(&mainView[i], &prefs); 60.2033 + } 60.2034 + if (preset >= 0) { 60.2035 + presetStart(); 60.2036 + presetAdd(preset); 60.2037 + for (int i = 0; i < MAX_PLAYERS; ++i) { 60.2038 + unpackOptions(&mainView[i], &prefs); 60.2039 + presetFinish(&mainView[i]); 60.2040 + } 60.2041 + } 60.2042 + // reload the skin 60.2043 + if (loadSkin(&mainView[0], skinName) < 0) { 60.2044 + mainCleanup(platView); 60.2045 + return EXIT_FAILURE; 60.2046 + }; 60.2047 + for (int i = 0; i < nPlayers; ++i) { 60.2048 + calcElev(&mainView[0]); 60.2049 + pcInit(&mainView[0], &prefs); 60.2050 + } 60.2051 + wantPause = 0; 60.2052 + platView[0].wantRecord = prefs.number[OPTIONS_AUTO_RECORD]; 60.2053 + LJMusic_start(skin.bgm, 60.2054 + 4096, // mix buffer size 60.2055 + bgmVolume); // volume scale 60.2056 + 60.2057 + LJView *const players[MAX_PLAYERS] = {&mainView[0], &mainView[1]}; 60.2058 + play(players, nPlayers); 60.2059 + LJMusic_stop(skin.bgm); 60.2060 + if (!wantsClose) { 60.2061 + gameOverAnimation(&platView[0], &p[0], 60.2062 + control[0].countdown <= 0); 60.2063 + debrief(&mainView[0]); 60.2064 + } 60.2065 + } 60.2066 + break; 60.2067 + 60.2068 + case TITLE_REPLAY: 60.2069 + { 60.2070 + if (cmdLineDemo) { 60.2071 + ustrzcpy(demoFilename, sizeof(demoFilename) - 1, 60.2072 + cmdLineDemo); 60.2073 + cmdLineDemo = NULL; 60.2074 + } else if (pickReplay() < 0) { 60.2075 + break; 60.2076 + } 60.2077 + 60.2078 + unpackOptions(&mainView[0], &prefs); 60.2079 + calcElev(&mainView[0]); 60.2080 + pcInit(&mainView[0], &prefs); 60.2081 + wantPause = 0; 60.2082 + platView[0].wantRecord = 0; 60.2083 + LJMusic_start(skin.bgm, 60.2084 + 4096, // mix buffer size 60.2085 + bgmVolume); // volume scale 60.2086 + 60.2087 + LJView *const players[2] = {&mainView[0], &mainView[1]}; 60.2088 + 60.2089 + p->gimmick = -1; // gimmick must be < 0 to activate replay 60.2090 + play(players, 1); 60.2091 + LJMusic_stop(skin.bgm); 60.2092 + if (p[0].gimmick < 0) { 60.2093 + badReplay(); 60.2094 + } else { 60.2095 + if (!wantsClose) { 60.2096 + gameOverAnimation(&platView[0], &p[0], 60.2097 + control[0].countdown <= 0); 60.2098 + debrief(&mainView[0]); 60.2099 + } 60.2100 + } 60.2101 + } 60.2102 + break; 60.2103 + 60.2104 + case TITLE_SKIN: 60.2105 + pickSkin(); 60.2106 + 60.2107 + // if resolution changed, reopen the window 60.2108 + { 60.2109 + int oldW = skinW; 60.2110 + int oldH = skinH; 60.2111 + loadSkinFile(&skin, skinName); 60.2112 + if (skinH != oldH || skinW != oldW) { 60.2113 + destroySystemBitmaps(&platView[0]); 60.2114 + openWindow(prefs.number[OPTIONS_WINDOWED]); 60.2115 + } 60.2116 + } 60.2117 + 60.2118 + // reload the skin 60.2119 + if (loadSkin(&mainView[0], skinName) < 0) { 60.2120 + mainCleanup(platView); 60.2121 + return EXIT_FAILURE; 60.2122 + }; 60.2123 + 60.2124 + // save options 60.2125 + saveOptions(&prefs); 60.2126 + break; 60.2127 + 60.2128 + case TITLE_OPTIONS: 60.2129 + { 60.2130 + int oldWindowed = prefs.number[OPTIONS_WINDOWED]; 60.2131 + options(&mainView[0], prefs.number); 60.2132 + if (oldWindowed != prefs.number[OPTIONS_WINDOWED]) { 60.2133 + destroySystemBitmaps(&platView[0]); 60.2134 + openWindow(prefs.number[OPTIONS_WINDOWED]); 60.2135 + } 60.2136 + } 60.2137 + saveOptions(&prefs); 60.2138 + 60.2139 + // reload the skin if the player changed the rotation system 60.2140 + unpackOptions(&mainView[0], &prefs); 60.2141 + if (wantsClose) { 60.2142 + break; 60.2143 + } 60.2144 + if (loadSkin(&mainView[0], skinName) < 0) { 60.2145 + mainCleanup(platView); 60.2146 + return EXIT_FAILURE; 60.2147 + }; 60.2148 + break; 60.2149 + 60.2150 + case TITLE_KEYS: 60.2151 + configureKeys(); 60.2152 + break; 60.2153 + 60.2154 + } 60.2155 + } 60.2156 + 60.2157 + mainCleanup(platView); 60.2158 + return 0; 60.2159 +} END_OF_MAIN();
61.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 61.2 +++ b/src/ljpc.h Fri Mar 13 00:39:12 2009 -0700 61.3 @@ -0,0 +1,119 @@ 61.4 +/* Header for PC frontend for LOCKJAW, an implementation of the Soviet Mind Game 61.5 + 61.6 +Copyright (C) 2006 Damian Yerrick <tepples+lj@spamcop.net> 61.7 + 61.8 +This work is free software; you can redistribute it and/or modify 61.9 +it under the terms of the GNU General Public License as published by 61.10 +the Free Software Foundation; either version 2 of the License, or 61.11 +(at your option) any later version. 61.12 + 61.13 +This program is distributed in the hope that it will be useful, 61.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 61.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 61.16 +GNU General Public License for more details. 61.17 + 61.18 +You should have received a copy of the GNU General Public License 61.19 +along with this program; if not, write to the Free Software 61.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 61.21 + 61.22 +Original game concept and design by Alexey Pajitnov. 61.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 61.24 +or The Tetris Company LLC. 61.25 + 61.26 +*/ 61.27 + 61.28 +#ifndef LJPC_H 61.29 +#define LJPC_H 61.30 +#include <allegro.h> 61.31 +#include "ljmusic.h" 61.32 +#include "ljplay.h" 61.33 +#include "pcjoy.h" 61.34 +#include "ljvorbis.h" 61.35 +#include "options.h" 61.36 + 61.37 +typedef struct LJPCSkin { 61.38 + BITMAP *bg; 61.39 + BITMAP *blocks; 61.40 + BITMAP *connBlocks; 61.41 + BITMAP *fullback; 61.42 + unsigned short blkW, blkH; 61.43 + unsigned short baseX, baseY; 61.44 + unsigned short pfElev; 61.45 + unsigned char transparentPF; 61.46 + unsigned char nextPos; 61.47 + unsigned char shiftScale; 61.48 + LJMusic *bgm; 61.49 +} LJPCSkin; 61.50 + 61.51 +typedef struct LJPCView 61.52 +{ 61.53 + BITMAP *back; 61.54 + LJPCSkin *skin; 61.55 + unsigned short baseX; 61.56 + unsigned char nextAbove; // number of next pieces above shadow 61.57 + 61.58 + // Platform-dependent sound 61.59 + unsigned char b2bcd1, b2bcd2; 61.60 + 61.61 + unsigned char wantRecord; 61.62 +} LJPCView; 61.63 + 61.64 +extern char skinName[PATH_MAX]; 61.65 +extern char ljblocksSRSName[PATH_MAX]; 61.66 +extern char ljblocksSegaName[PATH_MAX]; 61.67 +extern char ljconnSRSName[PATH_MAX]; 61.68 +extern char ljconnSegaName[PATH_MAX]; 61.69 +extern char ljbgName[PATH_MAX]; 61.70 +extern char bgmName[PATH_MAX]; 61.71 + 61.72 + 61.73 +#define N_GIMMICKS LJGM_N_GIMMICKS 61.74 + 61.75 + 61.76 +enum { 61.77 + LJNEXT_RIGHT = 0, 61.78 + LJNEXT_RIGHT_TAPER, 61.79 + LJNEXT_TOP, 61.80 + LJNEXT_N_STYLES 61.81 +}; 61.82 + 61.83 +/** 61.84 + * The number of persistent preferences. Must match the number of 61.85 + * fields in struct LJPrefsNamed. 61.86 + */ 61.87 + 61.88 +/** 61.89 + * Names of persistent preferences. 61.90 + * Order must be exactly the same as in 61.91 + * optionsMenu[] (options.c) 61.92 + */ 61.93 +enum { 61.94 + OPTIONS_NEXT_ABOVE = OPTIONS_MENU_LEN, 61.95 + OPTIONS_TRAILS, 61.96 + OPTIONS_AUTO_PAUSE, 61.97 + OPTIONS_AUTO_RECORD, 61.98 + OPTIONS_WINDOWED, 61.99 + PC_OPTIONS_MENU_LEN 61.100 +}; 61.101 +struct LJPrefs { 61.102 + unsigned char number[PC_OPTIONS_MENU_LEN]; 61.103 +}; 61.104 + 61.105 + 61.106 +/* set by display mode */ 61.107 +extern int pfBgColor, pfFgColor, bgColor, fgColor, hiliteColor; 61.108 +extern const FONT *aver32, *aver16; 61.109 +extern volatile char redrawWholeScreen; 61.110 +extern char autoPause; 61.111 + 61.112 +void saveScreen(int n); 61.113 +void ezPlaySample(const char *filename, int vol); 61.114 +int parse_ini_line(const char *in, char *key, char *var, char *val); 61.115 +int loadOptions(struct LJPrefs *prefs); 61.116 +void saveOptions(const struct LJPrefs *prefs); 61.117 +void options(LJView *v, unsigned char *prefs); 61.118 +void unpackOptions(LJView *v, const struct LJPrefs *prefs); 61.119 +void debrief(LJView *v); 61.120 + 61.121 + 61.122 +#endif
62.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 62.2 +++ b/src/ljplay.c Fri Mar 13 00:39:12 2009 -0700 62.3 @@ -0,0 +1,216 @@ 62.4 +/* Game loop frontend for LOCKJAW, an implementation of the Soviet Mind Game 62.5 + 62.6 +Copyright (C) 2006 Damian Yerrick <tepples+lj@spamcop.net> 62.7 + 62.8 +This work is free software; you can redistribute it and/or modify 62.9 +it under the terms of the GNU General Public License as published by 62.10 +the Free Software Foundation; either version 2 of the License, or 62.11 +(at your option) any later version. 62.12 + 62.13 +This program is distributed in the hope that it will be useful, 62.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 62.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 62.16 +GNU General Public License for more details. 62.17 + 62.18 +You should have received a copy of the GNU General Public License 62.19 +along with this program; if not, write to the Free Software 62.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 62.21 + 62.22 +Original game concept and design by Alexey Pajitnov. 62.23 +The Software is not sponsored or endorsed by Alexey Pajitnov[player], Elorg, 62.24 +or The Tetris Company LLC. 62.25 + 62.26 +*/ 62.27 + 62.28 +#include "ljplay.h" 62.29 +#ifndef WITH_REPLAY 62.30 +#define WITH_REPLAY 0 62.31 +#endif 62.32 + 62.33 +#if WITH_REPLAY 62.34 +#include "ljreplay.h" 62.35 +extern const char demoFilename[]; 62.36 + 62.37 +#endif 62.38 + 62.39 + 62.40 +void play(LJView *const v[], size_t nPlayers) { 62.41 + int canceled = 0; 62.42 + 62.43 + for (unsigned int player = 0; player < nPlayers; ++player) { 62.44 + LJField *const p = v[player]->field; 62.45 + LJControl *const ctl = v[player]->control; 62.46 + 62.47 + ctl->countdown = 6; 62.48 + ctl->presses = 0; 62.49 + 62.50 + /* Load replay if needed */ 62.51 +#if WITH_REPLAY 62.52 + if (p->gimmick < 0) { 62.53 + ctl->replaySrc = openReplay(demoFilename, p); 62.54 + if (!ctl->replaySrc) { 62.55 + return; 62.56 + } 62.57 + v[player]->backDirty = ~0; 62.58 + playRedrawScreen(v[player]); 62.59 + } else { 62.60 + ctl->replaySrc = 0; 62.61 + } 62.62 +#endif 62.63 + } 62.64 + if (!v[0]->control->replaySrc) { 62.65 + for (unsigned int player = 0; player < nPlayers; ++player) { 62.66 + LJField *const p = v[player]->field; 62.67 + newGame(p); 62.68 + initGimmicks(p); 62.69 + v[player]->nLockTimes = 0; 62.70 + v[player]->hideNext = 0; 62.71 + updField(v[player], ~0); 62.72 + } 62.73 + startingAnimation(v[0]); 62.74 + for (unsigned int player = 0; player < nPlayers; ++player) { 62.75 + v[player]->control->lastKeys = 0; 62.76 + v[player]->control->repressKeys = 0; 62.77 + blitField(v[player]); 62.78 + } 62.79 + } 62.80 + 62.81 + int lastTime = getTime(); 62.82 + 62.83 + while(v[0]->field->state != LJS_GAMEOVER && !canceled) { 62.84 + if (getTime() == lastTime) { 62.85 + yieldCPU(); 62.86 + } 62.87 + canceled |= ljHandleConsoleButtons(v[0]); 62.88 + while (getTime() - lastTime > 0) { 62.89 + for (unsigned int player = 0; player < nPlayers; ++player) { 62.90 + LJField *const p = v[player]->field; 62.91 + LJControl *const ctl = v[player]->control; 62.92 + LJInput in = {0, 0, 0, 0}; 62.93 + int curTime = getTime(); 62.94 + 62.95 + addKeysToInput(&in, readPad(player), p, ctl); 62.96 + 62.97 + // when returning from pause, catch up the speed control 62.98 + if (curTime - lastTime > 10 62.99 + || curTime - lastTime < -10) { 62.100 + lastTime = curTime; 62.101 + } 62.102 + 62.103 + { 62.104 + int irsAttempted = (p->sounds & (LJSND_SPAWN | LJSND_HOLD)) 62.105 + && in.rotation && ctl->initialRotate; 62.106 + 62.107 + // The next line does ALL the game logic. 62.108 + LJBits updatedRows = frame(p, &in) | gimmicks(p, ctl); 62.109 + 62.110 + v[player]->backDirty |= updatedRows; 62.111 + if (irsAttempted && (p->sounds & LJSND_ROTATE)) { 62.112 + p->sounds |= LJSND_IRS; 62.113 + } 62.114 + } 62.115 + 62.116 + // items is a partly view-based gimmick 62.117 + if (p->gimmick == LJGM_ITEMS && (p->sounds & LJSND_SPAWN)) { 62.118 + v[player]->hideNext = (v[player]->field->nPieces > 7); 62.119 + v[player]->frontDirty |= LJ_DIRTY_NEXT; 62.120 + } 62.121 + 62.122 + // five, four, three, two, one 62.123 + int curCountdown = ctl->countdown; 62.124 + if (p->gimmick == LJGM_ULTRA && p->gameTime >= 10500) { 62.125 + curCountdown = (int)(10859 - p->gameTime) / 60; 62.126 + } else if (p->gimmick == LJGM_BTYPE) { 62.127 + curCountdown = 40 - p->lines; 62.128 + } else if (p->gimmick == LJGM_BABY) { 62.129 + curCountdown = (309 - v[player]->control->presses) / 10; 62.130 + } else if (p->gimmick == LJGM_DRILL && (p->sounds & LJSND_LINE)) { 62.131 + int line = bfffo(p->tempRows); 62.132 + if (line < curCountdown) 62.133 + curCountdown = line; 62.134 + } 62.135 + 62.136 + // we have to wait for the new piece to come out 62.137 + // so that the score is credited properly 62.138 + if (curCountdown <= 0 62.139 + && (p->state == LJS_NEW_PIECE 62.140 + || p->state == LJS_FALLING 62.141 + || p->state == LJS_LANDED)) { 62.142 + p->state = LJS_GAMEOVER; 62.143 + } 62.144 + 62.145 + playSoundEffects(v[player], p->sounds, curCountdown); 62.146 + ctl->countdown = curCountdown; 62.147 + 62.148 + // Update speedometer 62.149 + if (p->sounds & LJSND_LOCK) { 62.150 + for (int i = N_SPEED_METER_PIECES - 2; i >= 0; --i) { 62.151 + v[player]->lockTime[i + 1] = v[player]->lockTime[i]; 62.152 + } 62.153 + v[player]->lockTime[0] = p->gameTime; 62.154 + if (v[player]->nLockTimes < N_SPEED_METER_PIECES) { 62.155 + ++v[player]->nLockTimes; 62.156 + } 62.157 + v[player]->frontDirty = LJ_DIRTY_SCORE; 62.158 + } 62.159 + 62.160 + // If the piece was just spawned, move the trail to the piece's 62.161 + // starting position and redraw next pieces. 62.162 + if (p->sounds & (LJSND_SPAWN | LJSND_HOLD)) { 62.163 + v[player]->trailY = p->y; 62.164 + v[player]->frontDirty |= LJ_DIRTY_NEXT; 62.165 + } 62.166 + } 62.167 + 62.168 + ++lastTime; 62.169 + } 62.170 + 62.171 + for (unsigned int player = 0; player < nPlayers; ++player) { 62.172 + LJField *const p = v[player]->field; 62.173 + if (p->state == LJS_GAMEOVER && v[player]->hidePF) { 62.174 + v[player]->hidePF = 0; 62.175 + v[player]->backDirty |= (1 << LJ_PF_VIS_HT) - 1; 62.176 + } 62.177 + 62.178 + updField(v[player], v[player]->backDirty); 62.179 + v[player]->frontDirty |= v[player]->backDirty; 62.180 + v[player]->backDirty = 0; 62.181 + 62.182 + // If piece is falling or landed, and it wasn't just spawned, 62.183 + // draw the piece and its shadow. 62.184 + 62.185 + if (p->sounds & (LJSND_SPAWN | LJSND_HOLD)) { 62.186 + // piece was just spawned, so don't draw the piece 62.187 + } 62.188 + // Otherwise, if the piece is falling or landed, draw it. 62.189 + else if (p->state == LJS_FALLING || p->state == LJS_LANDED 62.190 + || p->sounds & LJSND_LOCK) { 62.191 + if (v[player]->hideShadow != LJSHADOW_NO_FALLING) { 62.192 + if (p->state == LJS_FALLING && v[player]->hideShadow < LJSHADOW_NONE) { 62.193 + drawShadow(v[player]); 62.194 + } 62.195 + drawFallingPiece(v[player]); 62.196 + } 62.197 + } 62.198 + 62.199 + ljBeginDraw(v[player], getTime() - lastTime < 2); 62.200 + drawScore(v[player]); 62.201 + blitField(v[player]); 62.202 + ljEndDraw(v[player]); 62.203 + } 62.204 + } 62.205 + 62.206 +#if WITH_REPLAY 62.207 + { 62.208 + int player = 0; 62.209 + if (v[player]->control->replaySrc) { 62.210 + replayClose(v[player]->control->replaySrc); 62.211 + v[player]->control->replaySrc = NULL; 62.212 + } 62.213 + if (v[player]->control->replayDst) { 62.214 + replayClose(v[player]->control->replayDst); 62.215 + v[player]->control->replayDst = NULL; 62.216 + } 62.217 + } 62.218 +#endif 62.219 +}
63.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 63.2 +++ b/src/ljplay.h Fri Mar 13 00:39:12 2009 -0700 63.3 @@ -0,0 +1,57 @@ 63.4 +/* Platform hooks for LOCKJAW, an implementation of the Soviet Mind Game 63.5 + 63.6 +Copyright (C) 2006 Damian Yerrick <tepples+lj@spamcop.net> 63.7 + 63.8 +This work is free software; you can redistribute it and/or modify 63.9 +it under the terms of the GNU General Public License as published by 63.10 +the Free Software Foundation; either version 2 of the License, or 63.11 +(at your option) any later version. 63.12 + 63.13 +This program is distributed in the hope that it will be useful, 63.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 63.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 63.16 +GNU General Public License for more details. 63.17 + 63.18 +You should have received a copy of the GNU General Public License 63.19 +along with this program; if not, write to the Free Software 63.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 63.21 + 63.22 +Original game concept and design by Alexey Pajitnov. 63.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 63.24 +or The Tetris Company LLC. 63.25 + 63.26 +*/ 63.27 + 63.28 +#ifndef LJPLAY_H 63.29 +#define LJPLAY_H 63.30 +#include "lj.h" 63.31 +#include "ljcontrol.h" 63.32 + 63.33 +/** 63.34 + * Plays Lockjaw. 63.35 + * @param v view 63.36 + */ 63.37 +void play(LJView *const v[], size_t nPlayers); 63.38 + 63.39 +/* 63.40 + * Platform-native code must implement the following callbacks, 63.41 + * which will be described later: 63.42 + */ 63.43 +LJBits readPad(unsigned int player); 63.44 +void updField(const LJView *const v, LJBits rows); 63.45 +void startingAnimation(LJView *v); 63.46 +void blitField(LJView *v); 63.47 +int pauseGame(struct LJPCView *v); 63.48 +void playSoundEffects(LJView *v, LJBits sounds, int countdown); 63.49 +void drawShadow(LJView *v); 63.50 +void drawFallingPiece(LJView *v); 63.51 +void drawScore(LJView *v); 63.52 +int getTime(void); 63.53 +void yieldCPU(void); 63.54 +void ljBeginDraw(LJView *v, int sync); 63.55 +void ljEndDraw(LJView *v); 63.56 +int ljHandleConsoleButtons(LJView *v); 63.57 +void playRedrawScreen(LJView *v); 63.58 + 63.59 + 63.60 +#endif
64.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 64.2 +++ b/src/ljreplay.c Fri Mar 13 00:39:12 2009 -0700 64.3 @@ -0,0 +1,335 @@ 64.4 +/* Replay functionality for LOCKJAW, an implementation of the Soviet Mind Game 64.5 + 64.6 +Copyright (C) 2006-2008 Damian Yerrick <tepples+lj@spamcop.net> 64.7 + 64.8 +This work is free software; you can redistribute it and/or modify 64.9 +it under the terms of the GNU General Public License as published by 64.10 +the Free Software Foundation; either version 2 of the License, or 64.11 +(at your option) any later version. 64.12 + 64.13 +This program is distributed in the hope that it will be useful, 64.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 64.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 64.16 +GNU General Public License for more details. 64.17 + 64.18 +You should have received a copy of the GNU General Public License 64.19 +along with this program; if not, write to the Free Software 64.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 64.21 + 64.22 +Original game concept and design by Alexey Pajitnov. 64.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 64.24 +or The Tetris Company LLC. 64.25 + 64.26 +*/ 64.27 + 64.28 +#include "ljreplay.h" 64.29 +#include <stdio.h> 64.30 + 64.31 +#define FORMAT_VERSION 0x20080525 64.32 +#define LAST_FORMAT_VERSION 20080420 64.33 +#define DEBUG_OFFSETS 0 64.34 + 64.35 +#if DEBUG_OFFSETS 64.36 +#include <allegro.h> 64.37 +#endif 64.38 + 64.39 +struct LJReplayFrame { 64.40 + char length; 64.41 + char reserved; 64.42 + unsigned short keys; 64.43 + LJInput x; 64.44 +}; 64.45 + 64.46 +struct LJReplay { 64.47 + FILE *file; 64.48 +}; 64.49 + 64.50 +static void fput32(unsigned int src, FILE *dst) { 64.51 + fputc(src >> 24, dst); 64.52 + fputc(src >> 16, dst); 64.53 + fputc(src >> 8, dst); 64.54 + fputc(src, dst); 64.55 +} 64.56 + 64.57 +static unsigned int fget32(FILE *src) { 64.58 + int c0 = fgetc(src) & 0xFF; 64.59 + c0 = (c0 << 8) | (fgetc(src) & 0xFF); 64.60 + c0 = (c0 << 8) | (fgetc(src) & 0xFF); 64.61 + c0 = (c0 << 8) | (fgetc(src) & 0xFF); 64.62 + return c0; 64.63 +} 64.64 +static void fput16(unsigned int src, FILE *dst) { 64.65 + fputc(src >> 8, dst); 64.66 + fputc(src, dst); 64.67 +} 64.68 + 64.69 +static unsigned int fget16(FILE *src) { 64.70 + int c0 = fgetc(src) & 0xFF; 64.71 + c0 = (c0 << 8) | (fgetc(src) & 0xFF); 64.72 + return c0; 64.73 +} 64.74 + 64.75 +static void LJField_serialize(const LJField *p, FILE *fp) { 64.76 + fput32(FORMAT_VERSION, fp); 64.77 + 64.78 + for (int y = 0; y < LJ_PF_HT; ++y) { 64.79 + for (int x = 0; x < LJ_PF_WID; ++x) { 64.80 + fputc(p->b[y][x], fp); 64.81 + } 64.82 + } 64.83 + for (int y = 0; y < LJ_PF_HT; ++y) { 64.84 + for (int x = 0; x < LJ_PF_WID; ++x) { 64.85 + fputc(p->c[y][x], fp); 64.86 + } 64.87 + } 64.88 + fput32(p->clearedLines, fp); 64.89 + fput32(p->sounds, fp); 64.90 + fput32(p->tempRows, fp); 64.91 + for (int y = 0; y < 1 + LJ_NEXT_PIECES; ++y) { 64.92 + fputc(p->curPiece[y], fp); 64.93 + } 64.94 + for (int y = 0; y < 2 * MAX_BAG_LEN; ++y) { 64.95 + fputc(p->permuPiece[y], fp); 64.96 + } 64.97 + fputc(p->permuPhase, fp); 64.98 + for (int y = 0; y < LJ_MAX_LINES_PER_PIECE; ++y) { 64.99 + fput16(p->nLineClears[y], fp); 64.100 + } 64.101 + fput32(p->y, fp); 64.102 + fputc(p->state, fp); 64.103 + fputc(p->stateTime, fp); 64.104 + fputc(p->theta, fp); 64.105 + fputc(p->x, fp); 64.106 + fputc(p->hardDropY, fp); 64.107 + fputc(p->alreadyHeld, fp); 64.108 + fputc(p->isSpin, fp); 64.109 + fputc(p->nLinesThisPiece, fp); 64.110 + fputc(p->canRotate, fp); 64.111 + 64.112 +#if DEBUG_OFFSETS 64.113 + allegro_message("before score, offset = %u\n", (unsigned int)ftell(fp)); 64.114 +#endif 64.115 + fput32(p->score, fp); 64.116 + fput32(p->lines, fp); 64.117 + fput32(p->gameTime, fp); 64.118 + fput32(p->activeTime, fp); 64.119 + fput16(p->holdPiece, fp); 64.120 + fputc(p->chain, fp); 64.121 + fputc(p->garbage, fp); 64.122 + fputc(p->garbageX, fp); 64.123 + fput16(p->nPieces, fp); 64.124 + fput16(p->outGarbage, fp); 64.125 + 64.126 + fputc(p->gimmick, fp); 64.127 + fput32(p->speedState.level, fp); 64.128 + fput32(p->bpmCounter, fp); 64.129 + fput32(p->speedupCounter, fp); 64.130 + fput32(p->goalCount, fp); 64.131 + fput32(p->seed, fp); 64.132 + fput32(p->speed.gravity, fp); 64.133 + fputc(p->speedState.curve, fp); 64.134 + fputc(p->goalType, fp); 64.135 + fputc(p->speed.entryDelay, fp); 64.136 + fputc(p->areStyle, fp); 64.137 + fputc(p->lockReset, fp); 64.138 + fputc(p->speed.lockDelay, fp); 64.139 + fputc(p->speed.lineDelay, fp); 64.140 + fputc(p->ceiling, fp); 64.141 + fputc(p->enterAbove, fp); 64.142 + fputc(p->leftWall, fp); 64.143 + fputc(p->rightWall, fp); 64.144 + fputc(p->pieceSet, fp); 64.145 + fputc(p->randomizer, fp); 64.146 + fputc(p->rotationSystem, fp); 64.147 + fputc(p->garbageRandomness, fp); 64.148 + fputc(p->tSpinAlgo, fp); 64.149 + fputc(p->clearGravity, fp); 64.150 + fputc(p->gluing, fp); 64.151 + fputc(p->scoreStyle, fp); 64.152 + fputc(p->setLockDelay, fp); 64.153 + fputc(p->upwardKicks, fp); 64.154 + fputc(p->maxUpwardKicks, fp); 64.155 + fputc(p->setLineDelay, fp); 64.156 + fputc(p->garbageStyle, fp); 64.157 + fputc(p->holdStyle, fp); 64.158 + fputc(p->bottomBlocks, fp); 64.159 + 64.160 + fput16(p->multisquares, fp); 64.161 + fput16(p->monosquares, fp); 64.162 + 64.163 +#if DEBUG_OFFSETS 64.164 + allegro_message("final offset = %u\n", (unsigned int)ftell(fp)); 64.165 +#endif 64.166 +} 64.167 + 64.168 +static int LJField_deserialize(LJField *p, FILE *fp) { 64.169 + int format = fget32(fp); 64.170 + if (format != FORMAT_VERSION 64.171 + && format != LAST_FORMAT_VERSION) { 64.172 + return -1; 64.173 + } 64.174 + 64.175 + for (int y = 0; y < LJ_PF_HT; ++y) { 64.176 + for (int x = 0; x < LJ_PF_WID; ++x) { 64.177 + p->b[y][x] = fgetc(fp); 64.178 + } 64.179 + } 64.180 + for (int y = 0; y < LJ_PF_HT; ++y) { 64.181 + for (int x = 0; x < LJ_PF_WID; ++x) { 64.182 + p->c[y][x] = fgetc(fp); 64.183 + } 64.184 + } 64.185 + p->clearedLines = fget32(fp); 64.186 + p->sounds = fget32(fp); 64.187 + p->tempRows = fget32(fp); 64.188 + for (int y = 0; y < 1 + LJ_NEXT_PIECES; ++y) { 64.189 + p->curPiece[y] = fgetc(fp); 64.190 + } 64.191 + for (int y = 0; y < 2 * MAX_BAG_LEN; ++y) { 64.192 + p->permuPiece[y] = fgetc(fp); 64.193 + } 64.194 + p->permuPhase = fgetc(fp); 64.195 + for (int y = 0; y < LJ_MAX_LINES_PER_PIECE; ++y) { 64.196 + p->nLineClears[y] = fget16(fp); 64.197 + } 64.198 + p->y = fget32(fp); 64.199 + p->state = fgetc(fp); 64.200 + p->stateTime = fgetc(fp); 64.201 + p->theta = fgetc(fp); 64.202 + p->x = fgetc(fp); 64.203 + p->hardDropY = fgetc(fp); 64.204 + p->alreadyHeld = fgetc(fp); 64.205 + p->isSpin = fgetc(fp); 64.206 + p->nLinesThisPiece = fgetc(fp); 64.207 + p->canRotate = fgetc(fp); 64.208 + 64.209 +#if DEBUG_OFFSETS 64.210 + allegro_message("before score, offset = %u\n", (unsigned int)ftell(fp)); 64.211 +#endif 64.212 + p->score = fget32(fp); 64.213 + p->lines = fget32(fp); 64.214 + p->gameTime = fget32(fp); 64.215 + p->activeTime = fget32(fp); 64.216 + p->holdPiece = fget16(fp); 64.217 + p->chain = fgetc(fp); 64.218 + p->garbage = fgetc(fp); 64.219 + p->garbageX = fgetc(fp); 64.220 + p->nPieces = fget16(fp); 64.221 + p->outGarbage = fget16(fp); 64.222 + 64.223 + p->gimmick = fgetc(fp); 64.224 + p->speedState.level = fget32(fp); 64.225 + p->bpmCounter = fget32(fp); 64.226 + p->speedupCounter = fget32(fp); 64.227 + p->goalCount = fget32(fp); 64.228 + p->seed = fget32(fp); 64.229 + p->speed.gravity = fget32(fp); 64.230 + p->speedState.curve = fgetc(fp); 64.231 + p->goalType = fgetc(fp); 64.232 + p->speed.entryDelay = fgetc(fp); 64.233 + p->areStyle = fgetc(fp); 64.234 + p->lockReset = fgetc(fp); 64.235 + p->speed.lockDelay = fgetc(fp); 64.236 + p->speed.lineDelay = fgetc(fp); 64.237 + p->ceiling = fgetc(fp); 64.238 + p->enterAbove = fgetc(fp); 64.239 + p->leftWall = fgetc(fp); 64.240 + p->rightWall = fgetc(fp); 64.241 + p->pieceSet = fgetc(fp); 64.242 + p->randomizer = fgetc(fp); 64.243 + p->rotationSystem = fgetc(fp); 64.244 + p->garbageRandomness = fgetc(fp); 64.245 + p->tSpinAlgo = fgetc(fp); 64.246 + p->clearGravity = fgetc(fp); 64.247 + p->gluing = fgetc(fp); 64.248 + p->scoreStyle = fgetc(fp); 64.249 + p->setLockDelay = fgetc(fp); 64.250 + p->upwardKicks = fgetc(fp); 64.251 + p->maxUpwardKicks = fgetc(fp); 64.252 + p->setLineDelay = fgetc(fp); 64.253 + p->garbageStyle = fgetc(fp); 64.254 + p->holdStyle = fgetc(fp); 64.255 + p->bottomBlocks = fgetc(fp); 64.256 + 64.257 + if (format == FORMAT_VERSION) { 64.258 + p->multisquares = fget16(fp); 64.259 + p->monosquares = fget16(fp); 64.260 + } 64.261 +#if DEBUG_OFFSETS 64.262 + allegro_message("final offset = %u\n", (unsigned int)ftell(fp)); 64.263 +#endif 64.264 + return 0; 64.265 +} 64.266 + 64.267 + 64.268 +LJReplay *newReplay(const char *filename, LJField *p) { 64.269 + LJReplay *r = malloc(sizeof(struct LJReplay)); 64.270 + 64.271 + if (!r) { 64.272 + return NULL; 64.273 + } 64.274 + r->file = fopen(filename, "wb"); 64.275 + if (!r->file) { 64.276 + free(r); 64.277 + return NULL; 64.278 + } 64.279 + LJField_serialize(p, r->file); 64.280 + return r; 64.281 +} 64.282 + 64.283 +void replayRecord(LJReplay *r, LJBits keys, const LJInput *in) { 64.284 + fputc(0, r->file); 64.285 + fputc(0, r->file); 64.286 + fputc(keys >> 8, r->file); 64.287 + fputc(keys, r->file); 64.288 + fputc(in->rotation, r->file); 64.289 + fputc(in->movement, r->file); 64.290 + fputc(in->gravity, r->file); 64.291 + fputc(in->other, r->file); 64.292 +} 64.293 + 64.294 +LJReplay *openReplay(const char *filename, LJField *p) { 64.295 + LJReplay *r = malloc(sizeof(struct LJReplay)); 64.296 + 64.297 + if (!r) { 64.298 + return NULL; 64.299 + } 64.300 + r->file = fopen(filename, "rb"); 64.301 + if (!r->file) { 64.302 + free(r); 64.303 + return NULL; 64.304 + } 64.305 + 64.306 + /* This deserialization is still NOT robust. */ 64.307 + if (LJField_deserialize(p, r->file) < 0) { 64.308 + fclose(r->file); 64.309 + free(r); 64.310 + return 0; 64.311 + } 64.312 + return r; 64.313 +} 64.314 + 64.315 +int getReplayFrame(LJReplay *r, LJInput *d) { 64.316 + fgetc(r->file); 64.317 + fgetc(r->file); 64.318 + int keys = fgetc(r->file); 64.319 + 64.320 + if (keys == EOF) { 64.321 + return LJREPLAY_EOF; 64.322 + } 64.323 + keys = (keys << 8 & 0xFF) | (fgetc(r->file) & 0xFF); 64.324 + d->rotation = fgetc(r->file); 64.325 + d->movement = fgetc(r->file); 64.326 + d->gravity = fgetc(r->file); 64.327 + d->other = fgetc(r->file); 64.328 + return keys; 64.329 +} 64.330 + 64.331 +void replayClose(LJReplay *r) { 64.332 + if (r) { 64.333 + if (r->file) { 64.334 + fclose(r->file); 64.335 + } 64.336 + free(r); 64.337 + } 64.338 +}
65.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 65.2 +++ b/src/ljreplay.h Fri Mar 13 00:39:12 2009 -0700 65.3 @@ -0,0 +1,70 @@ 65.4 +/* Replay functionality for LOCKJAW, an implementation of the Soviet Mind Game 65.5 + 65.6 +Copyright (C) 2006 Damian Yerrick <tepples+lj@spamcop.net> 65.7 + 65.8 +This work is free software; you can redistribute it and/or modify 65.9 +it under the terms of the GNU General Public License as published by 65.10 +the Free Software Foundation; either version 2 of the License, or 65.11 +(at your option) any later version. 65.12 + 65.13 +This program is distributed in the hope that it will be useful, 65.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 65.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 65.16 +GNU General Public License for more details. 65.17 + 65.18 +You should have received a copy of the GNU General Public License 65.19 +along with this program; if not, write to the Free Software 65.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 65.21 + 65.22 +Original game concept and design by Alexey Pajitnov. 65.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 65.24 +or The Tetris Company LLC. 65.25 + 65.26 +*/ 65.27 +#ifndef LJREPLAY_H 65.28 +#define LJREPLAY_H 65.29 + 65.30 +#include "lj.h" 65.31 +#include "ljcontrol.h" 65.32 + 65.33 +#define LJREPLAY_EOF (-1) 65.34 + 65.35 +typedef struct LJReplay LJReplay; 65.36 + 65.37 +/** 65.38 + * Creates a new replay. 65.39 + * @param filename The name of the file to which the replay is recorded. 65.40 + * @param p The field that is observed. 65.41 + * @return A pointer to the replay object, 65.42 + * or NULL if allocation failed. 65.43 + */ 65.44 +LJReplay *newReplay(const char *filename, LJField *p); 65.45 + 65.46 +/** 65.47 + * Records a single frame of input in the replay. 65.48 + * If spawn or hold sound is played, records the new piece. 65.49 + * @param r The replay object. 65.50 + */ 65.51 +void replayRecord(LJReplay *r, LJBits keys, const LJInput *in); 65.52 + 65.53 +/** 65.54 + * Stops recording the replay and dumps it to the file. 65.55 + */ 65.56 +void replayClose(LJReplay *r); 65.57 + 65.58 +/** 65.59 + * Opens an existing replay. 65.60 + * @param filename The name of the file to which the replay is recorded. 65.61 + * @param p The field that is observed. 65.62 + * @return A pointer to the replay object, 65.63 + * or NULL if allocation failed. 65.64 + */ 65.65 +LJReplay *openReplay(const char *filename, LJField *p); 65.66 + 65.67 +/** 65.68 + * @param d The structure to be filled with input 65.69 + * @return The keys pressed, or REPLAY_EOF. 65.70 + */ 65.71 +int getReplayFrame(LJReplay *r, LJInput *d); 65.72 + 65.73 +#endif
66.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 66.2 +++ b/src/ljtypes.h Fri Mar 13 00:39:12 2009 -0700 66.3 @@ -0,0 +1,61 @@ 66.4 +/* Basic data types for LOCKJAW, an implementation of the Soviet Mind Game 66.5 + 66.6 +Copyright (C) 2006 Damian Yerrick <tepples+lj@spamcop.net> 66.7 + 66.8 +This work is free software; you can redistribute it and/or modify 66.9 +it under the terms of the GNU General Public License as published by 66.10 +the Free Software Foundation; either version 2 of the License, or 66.11 +(at your option) any later version. 66.12 + 66.13 +This program is distributed in the hope that it will be useful, 66.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 66.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 66.16 +GNU General Public License for more details. 66.17 + 66.18 +You should have received a copy of the GNU General Public License 66.19 +along with this program; if not, write to the Free Software 66.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 66.21 + 66.22 +Original game concept and design by Alexey Pajitnov. 66.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 66.24 +or The Tetris Company LLC. 66.25 + 66.26 +*/ 66.27 + 66.28 +#ifndef LJTYPES_H 66.29 +#define LJTYPES_H 66.30 + 66.31 +/** 66.32 + * A 16.16 signed number. Used commonly for Y piece positions. 66.33 + */ 66.34 +typedef signed int LJFixed; 66.35 + 66.36 +static inline signed int ljfixfloor(LJFixed f) __attribute__((const)); 66.37 + 66.38 +static inline signed int ljfixfloor(LJFixed f) { 66.39 + return f >> 16; 66.40 +} 66.41 + 66.42 +static inline LJFixed ljitofix(signed int f) __attribute__((const)); 66.43 + 66.44 +static inline LJFixed ljitofix(signed int f) { 66.45 + return f << 16; 66.46 +} 66.47 + 66.48 +/* 66.49 + * In most cases, macros are deprecated in favor of static inline functions 66.50 + * because the latter allow the compiler to perform more type checks. 66.51 + * However, the C language forbids calling a function in an inline 66.52 + * constructor, even if it is a static inline function with no side effects. 66.53 + * For example, GCC gives the misleading error message 66.54 + * "error: initializer element is not constant". 66.55 + * Therefore, I have to provide a second implementation of ljitofix() 66.56 + * as a macro for use in global variable initializers. 66.57 + */ 66.58 +#define LJITOFIX(f) ((LJFixed)((f) << 16)) 66.59 + 66.60 + 66.61 +typedef unsigned int LJBits; 66.62 + 66.63 +#endif 66.64 +
67.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 67.2 +++ b/src/ljvorbis.c Fri Mar 13 00:39:12 2009 -0700 67.3 @@ -0,0 +1,206 @@ 67.4 +/* 67.5 + 67.6 +ljvorbis.c 67.7 +Simple wrapper around vorbisfile for use with the Allegro library 67.8 +copyright 2006 Damian Yerrick 67.9 +based on vorbisfile example 67.10 +copyright 1994-2004 Xiph.Org Foundation 67.11 +licensed under a BSD style license set forth in COPYING-OGG.txt 67.12 + 67.13 +*/ 67.14 + 67.15 +#define ALLEGRO_USE_CONSOLE 67.16 +#include <stdio.h> 67.17 +#include "ljvorbis.h" 67.18 + 67.19 +#define VORBIS_BYTEDEPTH 2 // number of bytes per sample; 2 means 16-bit 67.20 +#define VORBIS_ENDIAN 0 // 0: x86/little; 2: ppc/big 67.21 +#define VORBIS_SIGNED 0 // 0: unsigned; 1: signed; Allegro uses unsigned 67.22 + 67.23 +LJVorbis *LJVorbis_open(const char *filename) { 67.24 + LJVorbis *ogg = malloc(sizeof(LJVorbis)); 67.25 + if (!ogg) { 67.26 + return NULL; 67.27 + } 67.28 + ogg->fp = fopen(filename, "rb"); 67.29 + if (!ogg->fp) { 67.30 + free(ogg); 67.31 + return NULL; 67.32 + } 67.33 + if (ov_open(ogg->fp, &(ogg->vf), NULL, 0) < 0) { 67.34 + fclose(ogg->fp); 67.35 + free(ogg); 67.36 + return NULL; 67.37 + } 67.38 + vorbis_info *vi=ov_info(&(ogg->vf),-1); 67.39 + ogg->rate = vi->rate; 67.40 + ogg->channels = vi->channels; 67.41 + ogg->length = ov_pcm_total(&(ogg->vf),-1); 67.42 + ogg->loopPoint = 0; 67.43 + ogg->voice = NULL; 67.44 + ogg->paused = 0; 67.45 + return ogg; 67.46 +} 67.47 + 67.48 +void LJVorbis_setLoop(LJVorbis *ogg, unsigned long int loopPoint) { 67.49 + if (ogg) { 67.50 + if (loopPoint < ogg->length) { 67.51 + ogg->loopPoint = loopPoint; 67.52 + } else { 67.53 + ogg->loopPoint = 0; 67.54 + } 67.55 + } 67.56 +} 67.57 + 67.58 +int LJVorbis_start(LJVorbis *ogg, int bufferSize, int vol, int pan) { 67.59 + if (ogg) { 67.60 + 67.61 + // if restarting, stop first 67.62 + if (ogg->voice) { 67.63 + LJVorbis_stop(ogg); 67.64 + } 67.65 + ogg->voice = play_audio_stream(bufferSize, 67.66 + 8 * VORBIS_BYTEDEPTH, 67.67 + ogg->channels > 1, 67.68 + ogg->rate, 67.69 + vol, pan); 67.70 + ogg->bufferSize = bufferSize; 67.71 + if (!ogg->voice) { 67.72 + return -1; 67.73 + } 67.74 + ov_pcm_seek(&(ogg->vf), 0); 67.75 + return 0; 67.76 + } 67.77 + return -1; 67.78 +} 67.79 + 67.80 +void LJVorbis_stop(LJVorbis *ogg) { 67.81 + if (ogg && ogg->voice) { 67.82 + stop_audio_stream(ogg->voice); 67.83 + ogg->voice = NULL; 67.84 + } 67.85 +} 67.86 + 67.87 +void LJVorbis_close(LJVorbis *ogg) { 67.88 + if (ogg) { 67.89 + LJVorbis_stop(ogg); 67.90 + ov_clear(&(ogg->vf)); // finalize decoder and close the file 67.91 + // thanks kesiev for reminding me that ov_clear closes the file itself 67.92 + free(ogg); 67.93 + } 67.94 +} 67.95 + 67.96 +void LJVorbis_pause(LJVorbis *ogg, int value) { 67.97 + if (ogg && ogg->voice) { 67.98 + int hwVoice = ogg->voice->voice; 67.99 + voice_set_frequency(hwVoice, value ? 0 : ogg->rate); 67.100 + ogg->paused = value ? 1 : 0; 67.101 + } 67.102 +} 67.103 + 67.104 +int LJVorbis_poll(LJVorbis *ogg) { 67.105 + if (!ogg || !ogg->voice) { 67.106 + return -1; 67.107 + } 67.108 + char *buf = get_audio_stream_buffer(ogg->voice); 67.109 + int eofReached = 0; 67.110 + 67.111 + if (buf) { 67.112 + // the number of bytes left in this buffer 67.113 + long int bytesLeft = ogg->bufferSize * VORBIS_BYTEDEPTH * ogg->channels; 67.114 + 67.115 + while (bytesLeft > 0) { 67.116 + long ret=ov_read(&(ogg->vf), 67.117 + buf, 67.118 + bytesLeft, 67.119 + VORBIS_ENDIAN, 67.120 + VORBIS_BYTEDEPTH, 67.121 + VORBIS_SIGNED, 67.122 + &(ogg->bitstream)); 67.123 + if (ret == 0) { 67.124 + // try to seek back to the beginning of the file 67.125 + int pcmErr = ov_pcm_seek(&(ogg->vf), ogg->loopPoint); 67.126 + if (pcmErr) { 67.127 + /* EOF */ 67.128 + eofReached = 1; 67.129 + bytesLeft = 0; 67.130 + } 67.131 + } else if (ret < 0) { 67.132 + // Stream error. Just ignore it. 67.133 + } else { 67.134 + /* FIXME: handle sample rate changes, etc */ 67.135 + // advance forward in the buffer 67.136 + buf += ret; 67.137 + bytesLeft -= ret; 67.138 + } 67.139 + } 67.140 + free_audio_stream_buffer(ogg->voice); 67.141 + } 67.142 + return eofReached; 67.143 +} 67.144 + 67.145 +#ifdef LJVORBIS_DEMO 67.146 +int main(){ 67.147 + int eofReached = 0; 67.148 + LJVorbis *ogg = LJVorbis_open("AM-3P.ogg"); 67.149 + 67.150 + if (!ogg) { 67.151 + fprintf(stderr, "Could not open AM-3P.ogg.\n"); 67.152 + exit(1); 67.153 + } 67.154 + LJVorbis_setLoop(ogg, 650772); 67.155 + 67.156 + if (allegro_init() < 0 67.157 + || install_timer() < 0 67.158 + || set_gfx_mode(GFX_SAFE, 320, 200, 0, 0) < 0 67.159 + || install_keyboard() < 0 67.160 + || install_sound(DIGI_AUTODETECT, MIDI_NONE, NULL) < 0) { 67.161 + allegro_exit(); 67.162 + LJVorbis_close(ogg); 67.163 + fprintf(stderr, "Could not start Allegro library: %s\n", allegro_error); 67.164 + exit(1); 67.165 + } 67.166 + 67.167 + /* Throw the comments plus a few lines about the bitstream we're 67.168 + decoding */ 67.169 + if (0) 67.170 + { 67.171 + char line1[80], line2[80]; 67.172 + 67.173 + usprintf(line1,"%d channels, %u Hz", ogg->channels, ogg->rate); 67.174 + usprintf(line2,"length: %lu samples", ogg->length); 67.175 + alert(line1, line2, "ready?", 67.176 + "Play", 0, 13, 0); 67.177 + } 67.178 + 67.179 + if (LJVorbis_start(ogg, 1024, 192, 128) < 0) { 67.180 + LJVorbis_close(ogg); 67.181 + alert("Could not allocate voice", 67.182 + "for playing audio.", 67.183 + "", 67.184 + "OK", 0, 13, 0); 67.185 + exit(1); 67.186 + } 67.187 + 67.188 + while(!eofReached){ 67.189 + eofReached = LJVorbis_poll(ogg); 67.190 + rest(16); 67.191 + 67.192 + if (keypressed()) { 67.193 + int scancode; 67.194 + ureadkey(&scancode); 67.195 + 67.196 + if (scancode == KEY_P) { 67.197 + LJVorbis_pause(ogg, !ogg->paused); 67.198 + } else if (scancode == KEY_ESC) { 67.199 + eofReached = 1; 67.200 + } 67.201 + } 67.202 + } 67.203 + 67.204 + /* cleanup */ 67.205 + LJVorbis_close(ogg); 67.206 + return(0); 67.207 +} 67.208 +END_OF_MAIN(); 67.209 +#endif
68.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 68.2 +++ b/src/ljvorbis.h Fri Mar 13 00:39:12 2009 -0700 68.3 @@ -0,0 +1,76 @@ 68.4 +/* 68.5 + 68.6 +ljvorbis.h 68.7 +Simple wrapper around vorbisfile for use with the Allegro library 68.8 +copyright 2006 Damian Yerrick 68.9 +based on vorbisfile example 68.10 +copyright 1994-2004 Xiph.Org Foundation 68.11 +licensed under a BSD style license set forth in COPYING-OGG.txt 68.12 + 68.13 +*/ 68.14 + 68.15 +#ifndef LJVORBIS_H 68.16 +#define LJVORBIS_H 68.17 + 68.18 +#include <vorbis/codec.h> 68.19 +#include <vorbis/vorbisfile.h> 68.20 +#include <allegro.h> 68.21 + 68.22 +typedef struct LJVorbis { 68.23 + FILE *fp; 68.24 + AUDIOSTREAM *voice; 68.25 + OggVorbis_File vf; 68.26 + int bitstream; 68.27 + unsigned int bufferSize; 68.28 + unsigned int rate; 68.29 + unsigned long int length; 68.30 + unsigned long int loopPoint; 68.31 + unsigned char channels; 68.32 + unsigned char paused; 68.33 +} LJVorbis; 68.34 + 68.35 +/** 68.36 + * Creates a new LJVorbis instance. 68.37 + * @param filename the name of the .ogg file to open 68.38 + * @return an LJVorbis pointer 68.39 + */ 68.40 +LJVorbis *LJVorbis_open(const char *filename); 68.41 + 68.42 +/** 68.43 + * Sets the loop point of an LJVorbis. If it is past the end 68.44 + * of the file, sets the loop point to the start of the file. 68.45 + * @param loopPoint the sample number to seek back to 68.46 + */ 68.47 +void LJVorbis_setLoop(LJVorbis *ogg, unsigned long int loopPoint); 68.48 + 68.49 +/** 68.50 + * Starts or restarts an LJVorbis playing in a new Allegro voice. 68.51 + * @param bufferSize the size of the Allegro audio buffer in samples 68.52 + * @param vol the Allegro volume (0-255?) 68.53 + * @param pan the Allegro pan value (0=left, 256=right) 68.54 + */ 68.55 +int LJVorbis_start(LJVorbis *ogg, int bufferSize, int vol, int pan); 68.56 + 68.57 +/** 68.58 + * Stops an LJVorbis and frees its Allegro voice. 68.59 + */ 68.60 +void LJVorbis_stop(LJVorbis *ogg); 68.61 + 68.62 +/** 68.63 + * Destroys an LJVorbis instance entirely. 68.64 + */ 68.65 +void LJVorbis_close(LJVorbis *ogg); 68.66 + 68.67 +/** 68.68 + * Pauses or resumes an LJVorbis. 68.69 + * @param value 0 to pause, or nonzero to resume 68.70 + */ 68.71 +void LJVorbis_pause(LJVorbis *ogg, int value); 68.72 + 68.73 +/** 68.74 + * Processes an LJVorbis 68.75 + * Must be called periodically, at least once every bufferSize samples. 68.76 + */ 68.77 +int LJVorbis_poll(LJVorbis *ogg); 68.78 + 68.79 +#endif
69.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 69.2 +++ b/src/macro.c Fri Mar 13 00:39:12 2009 -0700 69.3 @@ -0,0 +1,263 @@ 69.4 +/* Input handling for LOCKJAW Tetromino Game 69.5 + 69.6 +Copyright (C) 2006 Damian Yerrick <tepples+lj@spamcop.net> 69.7 + 69.8 +This work is free software; you can redistribute it and/or modify 69.9 +it under the terms of the GNU General Public License as published by 69.10 +the Free Software Foundation; either version 2 of the License, or 69.11 +(at your option) any later version. 69.12 + 69.13 +This program is distributed in the hope that it will be useful, 69.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 69.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 69.16 +GNU General Public License for more details. 69.17 + 69.18 +You should have received a copy of the GNU General Public License 69.19 +along with this program; if not, write to the Free Software 69.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 69.21 + 69.22 +Original game concept and design by Alexey Pajitnov. 69.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 69.24 +or The Tetris Company LLC. 69.25 + 69.26 +*/ 69.27 +#include "lj.h" 69.28 +#include "ljcontrol.h" 69.29 +#include "ljreplay.h" 69.30 +#include "pcjoy.h" 69.31 + 69.32 +// first is rotation (+1 = 90 deg clockwise) 69.33 +// second is movement (+1 = right 1 block) 69.34 +// third is gravity (+1 = down 1/8 block) 69.35 +// fourth is extra actions (hold, lock) 69.36 +LJInput macros[8] = { 69.37 + { -1, 0, 0, 0 }, // default: alt. rotate left 69.38 + { -2, 0, 0, 0 }, // default: rotate left twice 69.39 + { 0, -9, 0, 0 }, // default: far left 69.40 + { 0, 9, 0, 0 }, // default: far right 69.41 + { 0, 0, 20*8, 0 }, // default: firm drop 69.42 + { 0, 0, 0, LJI_HOLD }, // default: alternate hold 69.43 + { 0, 0, 0, 0 }, 69.44 + { 0, 0, 0, 0 } 69.45 +}; 69.46 + 69.47 +void addMacrosToInput(LJInput *dst, LJBits keys) { 69.48 + int rotation = dst->rotation; 69.49 + int movement = dst->movement; 69.50 + int gravity = dst->gravity; 69.51 + int other = dst->other; 69.52 + int macro; 69.53 + 69.54 + keys >>= 8; 69.55 + for (macro = 0; 69.56 + macro < 8; 69.57 + keys >>= 1, ++macro) { 69.58 + if (keys & 1) { 69.59 + rotation += macros[macro].rotation; 69.60 + movement += macros[macro].movement; 69.61 + gravity += macros[macro].gravity; 69.62 + other |= macros[macro].other; 69.63 + } 69.64 + } 69.65 + 69.66 + // Clip rotation to [-3, +3] 69.67 + rotation -= rotation / 4 * 4; 69.68 + 69.69 + // Clip movement to playfield width 69.70 + if (movement < (int)-LJ_PF_WID) { 69.71 + movement = -LJ_PF_WID; 69.72 + } else if (movement > (int)LJ_PF_WID) { 69.73 + movement = LJ_PF_WID; 69.74 + } 69.75 + 69.76 + // Clip gravity to playfield height 69.77 + if (gravity > LJ_PF_HT * 8) { 69.78 + gravity = LJ_PF_HT * 8; 69.79 + } 69.80 + 69.81 + dst->rotation = rotation; 69.82 + dst->movement = movement; 69.83 + dst->gravity = gravity; 69.84 + dst->other = other; 69.85 +} 69.86 + 69.87 +static const LJFixed softDropSpeeds[3] = { 69.88 + LJITOFIX(1), 69.89 + LJITOFIX(1)/2, 69.90 + LJITOFIX(1)/3 69.91 +}; 69.92 + 69.93 +void addKeysToInput(LJInput *dst, LJBits keys, const LJField *p, LJControl *c) { 69.94 + int actualKeys = keys; 69.95 + 69.96 + if (c->replaySrc) { 69.97 + keys = getReplayFrame(c->replaySrc, dst); 69.98 + if (keys == LJREPLAY_EOF) { 69.99 + keys = actualKeys; 69.100 + replayClose(c->replaySrc); 69.101 + c->replaySrc = NULL; 69.102 + } 69.103 + } 69.104 + 69.105 + int lastFrameKeys = c->lastKeys; 69.106 + 69.107 + // If diagonal presses are disabled, ignore any changes 69.108 + if (!c->allowDiagonals 69.109 + && (keys & (VKEY_UP | VKEY_DOWN)) 69.110 + && (keys & (VKEY_LEFT | VKEY_RIGHT))) { 69.111 + keys &= ~(VKEY_UP | VKEY_DOWN | VKEY_LEFT | VKEY_RIGHT); 69.112 + keys |= lastFrameKeys 69.113 + & (VKEY_UP | VKEY_DOWN | VKEY_LEFT | VKEY_RIGHT); 69.114 + } 69.115 + 69.116 + LJBits newKeys = keys & ~lastFrameKeys; 69.117 + c->lastKeys = keys; 69.118 + 69.119 + // Count presses for Baboo!, excluding console buttons 69.120 + c->presses += countOnes(newKeys & 0x0000FFFF); 69.121 + 69.122 + // Only once the side effect of counting presses for Baboo! 69.123 + // is complete can we break out of a replay. 69.124 + if (c->replaySrc) { 69.125 + return; 69.126 + } 69.127 + 69.128 + LJBits releasedKeys = ~keys & lastFrameKeys; 69.129 + 69.130 + // At this point, c->lastKeys holds the keys actually held 69.131 + // by the player this frame, and lastFrameKeys holds the keys 69.132 + // actually held by the player last frame. 69.133 + 69.134 + // Handle keys that must be re-pressed 69.135 + releasedKeys &= ~c->repressKeys; 69.136 + c->repressKeys &= keys; 69.137 + keys &= ~c->repressKeys; 69.138 + 69.139 + // If locking in a mode without ARE, require 69.140 + // down to be re-pressed before next piece 69.141 + if (p->sounds & LJSND_LOCK 69.142 + && p->speed.entryDelay <= c->dasDelay) { 69.143 + c->repressKeys |= VKEY_DOWN; 69.144 + 69.145 + // Treat up the same way when hard drop lock is set to lock on release. 69.146 + if (c->hardDropLock != LJZANGI_SLIDE 69.147 + || p->lockReset == LJLOCK_NOW 69.148 + || p->speed.lockDelay <= c->dasDelay) { 69.149 + c->repressKeys |= VKEY_UP | VKEY_MACRO(4); 69.150 + } 69.151 + } 69.152 + 69.153 + // Initial Rotation System (IRS): 69.154 + // When a piece spawns from next or hold, and a rotation or macro 69.155 + // key is held, treat the key as if they had just been pressed. 69.156 + // Treat hard drop the same way when ARE is turned on. 69.157 + if ((p->sounds & (LJSND_SPAWN | LJSND_HOLD)) 69.158 + && c->initialRotate) { 69.159 + newKeys |= c->lastKeys 69.160 + & (VKEY_ROTL | VKEY_ROTR | VKEY_MACROS); 69.161 + if (p->speed.entryDelay > 0) { 69.162 + newKeys |= c->lastKeys 69.163 + & VKEY_UP; 69.164 + } 69.165 + } 69.166 + 69.167 + // if we're pretending that keys are not pressed, 69.168 + // pretend consistently 69.169 + newKeys &= keys; 69.170 + 69.171 + // TGM does not perform sideways movement on 69.172 + // the first frame after a piece is spawned. 69.173 + if (c->initialDAS == 0 && 69.174 + (p->sounds & (LJSND_SPAWN | LJSND_HOLD))) { 69.175 + 69.176 + } else if (keys & VKEY_LEFT) { 69.177 + if (c->dasCounter > -(int)c->dasDelay) { 69.178 + if (c->dasCounter >= 0) { 69.179 + c->dasCounter = -1; 69.180 + dst->movement = -1; 69.181 + } else { 69.182 + c->dasCounter -= 1; 69.183 + } 69.184 + } else { 69.185 + int dasSpeed = c->dasSpeed; 69.186 + if (dasSpeed) { 69.187 + dst->movement = -1; 69.188 + c->dasCounter += dasSpeed - 1; 69.189 + } else { 69.190 + dst->movement = -(int)LJ_PF_WID; 69.191 + } 69.192 + } 69.193 + } else if (keys & VKEY_RIGHT) { 69.194 + if (c->dasCounter < c->dasDelay) { 69.195 + if (c->dasCounter <= 0) { 69.196 + c->dasCounter = 1; 69.197 + dst->movement = 1; 69.198 + } else { 69.199 + c->dasCounter += 1; 69.200 + } 69.201 + } else { 69.202 + int dasSpeed = c->dasSpeed; 69.203 + if (dasSpeed) { 69.204 + dst->movement = 1; 69.205 + c->dasCounter -= dasSpeed - 1; 69.206 + } else { 69.207 + dst->movement = (int)LJ_PF_WID; 69.208 + } 69.209 + } 69.210 + } else { 69.211 + c->dasCounter = 0; 69.212 + } 69.213 + 69.214 + if(keys & VKEY_DOWN) { 69.215 + int g = softDropSpeeds[c->softDropSpeed]; 69.216 + 69.217 + // dither speed to 1/8G units 69.218 + g += ljitofix(p->gameTime % 3) / 24; 69.219 + dst->gravity += g >> 13; 69.220 + 69.221 + if ((newKeys & VKEY_DOWN) 69.222 + || c->softDropLock == LJZANGI_LOCK) { 69.223 + dst->other |= LJI_LOCK; 69.224 + } 69.225 + } 69.226 + 69.227 + if (newKeys & VKEY_ROTL) { 69.228 + dst->rotation -= 1; 69.229 + } 69.230 + if (newKeys & VKEY_ROTR) { 69.231 + dst->rotation += 1; 69.232 + } 69.233 + if (newKeys & VKEY_HOLD) { 69.234 + dst->other |= LJI_HOLD; 69.235 + } 69.236 + if (newKeys & VKEY_UP) { 69.237 + dst->gravity = LJ_PF_HT << 3; 69.238 + if (p->state == LJS_LANDED 69.239 + || c->hardDropLock == LJZANGI_LOCK) { 69.240 + dst->other |= LJI_LOCK; 69.241 + } 69.242 + } 69.243 + 69.244 + if (c->hardDropLock == LJZANGI_LOCK_RELEASE) { 69.245 + if (releasedKeys & VKEY_UP) { 69.246 + dst->other |= LJI_LOCK; 69.247 + } 69.248 + } 69.249 + if (c->softDropLock == LJZANGI_LOCK_RELEASE) { 69.250 + if (releasedKeys & VKEY_DOWN) { 69.251 + dst->other |= LJI_LOCK; 69.252 + } 69.253 + } 69.254 + 69.255 + addMacrosToInput(dst, newKeys); 69.256 + 69.257 + // Baboo! ends with a hard drop 69.258 + if (p->gimmick == LJGM_BABY && c->presses >= 300) { 69.259 + dst->gravity = LJ_PF_HT << 3; 69.260 + dst->other |= LJI_LOCK; 69.261 + } 69.262 + 69.263 + if (c->replayDst) { 69.264 + replayRecord(c->replayDst, actualKeys, dst); 69.265 + } 69.266 +}
70.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 70.2 +++ b/src/old_pc_options.c Fri Mar 13 00:39:12 2009 -0700 70.3 @@ -0,0 +1,436 @@ 70.4 +/* PC option screen for LOCKJAW, an implementation of the Soviet Mind Game 70.5 + 70.6 +Copyright (C) 2006 Damian Yerrick <tepples+lj@spamcop.net> 70.7 + 70.8 +This work is free software; you can redistribute it and/or modify 70.9 +it under the terms of the GNU General Public License as published by 70.10 +the Free Software Foundation; either version 2 of the License, or 70.11 +(at your option) any later version. 70.12 + 70.13 +This program is distributed in the hope that it will be useful, 70.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 70.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 70.16 +GNU General Public License for more details. 70.17 + 70.18 +You should have received a copy of the GNU General Public License 70.19 +along with this program; if not, write to the Free Software 70.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 70.21 + 70.22 +Original game concept and design by Alexey Pajitnov. 70.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 70.24 +or The Tetris Company LLC. 70.25 + 70.26 +*/ 70.27 + 70.28 +#include "ljpc.h" 70.29 +#include <ctype.h> 70.30 +#include "ljpath.h" 70.31 +#include <string.h> 70.32 +#include "options.h" 70.33 +#include "ljlocale.h" 70.34 + 70.35 +#define OPTIONS_OFFSET 0 70.36 +#define optionsMenu (commonOptionsMenu + OPTIONS_OFFSET) 70.37 + 70.38 + 70.39 + 70.40 +#if 0 70.41 +static int getOptionValueByName(int row, const char *value) { 70.42 + for (int x = 0; x < optionsMenu[row].nValues; ++x) { 70.43 + if (!ustrcmp(value, optionsMenu[row].valueNames[x])) { 70.44 + return x + optionsMenu[row].minValue; 70.45 + } 70.46 + } 70.47 + return -1; 70.48 +} 70.49 + 70.50 +static void setOptionValueByNumber(struct LJPrefs *prefs, 70.51 + int row, const char *value) { 70.52 + if (value[0] >= '0' && value[0] <= '9') { 70.53 + int num = atoi(value); 70.54 + if (num >= optionsMenu[row].minValue 70.55 + && num < optionsMenu[row].minValue + optionsMenu[row].nValues) { 70.56 + prefs->number[row] = num; 70.57 + } 70.58 + } 70.59 +} 70.60 + 70.61 +static void setOptionValueByName(struct LJPrefs *prefs, 70.62 + int row, const char *value) { 70.63 + int num = getOptionValueByName(row, value); 70.64 + 70.65 + if (num >= 0) { 70.66 + prefs->number[row] = num; 70.67 + } 70.68 +} 70.69 + 70.70 +static void setOptionValueByBoolean(struct LJPrefs *prefs, 70.71 + int row, const char *value) { 70.72 + int num = atoi(value); 70.73 + 70.74 + if (num >= 0) { 70.75 + prefs->number[row] = num ? 1 : 0; 70.76 + } 70.77 +} 70.78 +#endif 70.79 + 70.80 +/* parse_ini_line ******************* 70.81 +*/ 70.82 +int parse_ini_line(const char *in, char *key, char *var, char *val) 70.83 +{ 70.84 + int c; 70.85 + char *kstart = key; 70.86 + 70.87 + /* Skip whitespace before key */ 70.88 + while(*in && isspace(*in)) 70.89 + in++; 70.90 + 70.91 + /* Parse key */ 70.92 + if(*in == '[') /* if we have a new key, load it */ 70.93 + { 70.94 + in++; 70.95 + 70.96 + /* Skip whitespace before actual key */ 70.97 + while(*in && isspace(*in)) 70.98 + in++; 70.99 + 70.100 + for(c = *in++; 70.101 + c != 0 && c != ']' && c != '\n' && c != '\r'; 70.102 + c = *in++) 70.103 + { 70.104 + if(!isspace(c)) 70.105 + *key++ = c; 70.106 + } 70.107 + *key = 0; 70.108 + /* Strip whitespace after key */ 70.109 + do { 70.110 + *key-- = 0; 70.111 + } while(key >= kstart && isspace(*key)); 70.112 + } 70.113 + 70.114 + /* Skip whitespace before variable */ 70.115 + while(*in && isspace(*in)) 70.116 + in++; 70.117 + if(*in == 0) /* if there is no variable, don't do anything */ 70.118 + return 0; 70.119 + 70.120 + for(c = *in++; 70.121 + c != 0 && c != '=' && c != '\n' && c != '\r'; 70.122 + c = *in++) 70.123 + { 70.124 + if(!isspace(c)) 70.125 + { 70.126 + *var++ = c; 70.127 + } 70.128 + } 70.129 + *var = 0; 70.130 + 70.131 + /* Skip whitespace before value */ 70.132 + while(*in && isspace(*in)) 70.133 + in++; 70.134 + 70.135 + /* Get value */ 70.136 + kstart = val; 70.137 + for(c = *in++; 70.138 + c != 0 && c != '\n' && c != '\r'; 70.139 + c = *in++) 70.140 + { 70.141 + *val++ = c; 70.142 + } 70.143 + /* Strip whitespace after value */ 70.144 + do { 70.145 + *val-- = 0; 70.146 + } while(val >= kstart && isspace(*val)); 70.147 + 70.148 + return 0; 70.149 +} 70.150 + 70.151 +static const char ljIniName[] = "lj.ini"; 70.152 + 70.153 +/** 70.154 + * Finds a FourCC in a list. 70.155 + * @param needle the fourCC to search for 70.156 + * @param haystack a list of fourCCs 70.157 + * @param haystackLen the length of this list 70.158 + * @return the index within haystack where needle was found 70.159 + * or -1 if not found 70.160 + */ 70.161 +int findFourCC(const char *needle, 70.162 + const FourCC *haystack, size_t haystackLen) { 70.163 + for (size_t i = 0; i < haystackLen; ++i) { 70.164 + if (!strncmp(haystack[i].c, needle, 4)) { 70.165 + return i; 70.166 + } 70.167 + } 70.168 + return -1; 70.169 +} 70.170 + 70.171 +void loadOption(struct LJPrefs *prefs, 70.172 + const char *name, const char *value) { 70.173 + for (int i = 0; i < PC_OPTIONS_MENU_LEN; ++i) { 70.174 + if (!strncmp(optionsMenu[i].name.c, name, 4)) { 70.175 + if (isdigit(value[0])) { 70.176 + unsigned long int n = strtoul(value, NULL, 10); 70.177 + if (n >= optionsMenu[i].minValue 70.178 + && n < optionsMenu[i].minValue + optionsMenu[i].nValues) { 70.179 + prefs->number[i] = n; 70.180 + } else { 70.181 + allegro_message("ignoring out of range %s=%lu\n", 70.182 + name, n); 70.183 + } 70.184 + } else if (optionsMenu[i].valueNames) { 70.185 + int nValueNames = (optionsMenu[i].style == OPTSTYLE_FRAMES 70.186 + ? 2 70.187 + : optionsMenu[i].nValues); 70.188 + int foundValue = findFourCC(value, optionsMenu[i].valueNames, nValueNames); 70.189 + if (optionsMenu[i].style == OPTSTYLE_FRAMES 70.190 + && foundValue > 0) { 70.191 + foundValue = optionsMenu[i].nValues - 1; 70.192 + } 70.193 + prefs->number[i] = foundValue + optionsMenu[i].minValue; 70.194 + } 70.195 + return; 70.196 + } 70.197 + } 70.198 +} 70.199 + 70.200 +int loadOptions(struct LJPrefs *prefs) { 70.201 + FILE *fp = ljfopen(ljIniName, "rt"); 70.202 + char key[1024], var[1024], val[1024], input_buf[1024]; 70.203 + 70.204 + if (!fp) return 0; 70.205 + 70.206 + key[0] = 0; 70.207 + var[0] = 0; 70.208 + val[0] = 0; 70.209 + 70.210 + while(fgets (input_buf, sizeof(input_buf), fp)) { 70.211 + parse_ini_line(input_buf, key, var, val); 70.212 + 70.213 + if(!ustrcmp ("Skin", var)) { 70.214 + ustrzcpy(skinName, sizeof(skinName) - 1, val); 70.215 + } else { 70.216 + loadOption(prefs, var, val); 70.217 + } 70.218 + } 70.219 + fclose(fp); 70.220 + 70.221 + if (prefs->number[OPTIONS_SIDEWAYS_DELAY] < prefs->number[OPTIONS_SIDEWAYS_SPEED]) { 70.222 + prefs->number[OPTIONS_SIDEWAYS_DELAY] = prefs->number[OPTIONS_SIDEWAYS_SPEED]; 70.223 + } 70.224 + 70.225 + return 0; 70.226 +} 70.227 + 70.228 +void saveOption(int i, int n, FILE *out) { 70.229 + char name[8], value[8]; 70.230 + int nameIdx = -1; 70.231 + 70.232 + strncpy(name, optionsMenu[i].name.c, 4); 70.233 + name[4] = 0; 70.234 + 70.235 + if (optionsMenu[i].valueNames) { 70.236 + if (optionsMenu[i].style == OPTSTYLE_FRAMES) { 70.237 + if (n == optionsMenu[i].minValue) { 70.238 + nameIdx = 0; 70.239 + } else if (n == optionsMenu[i].minValue + optionsMenu[i].nValues - 1) { 70.240 + nameIdx = 1; 70.241 + } 70.242 + } else { 70.243 + nameIdx = n - optionsMenu[i].minValue; 70.244 + } 70.245 + if (nameIdx >= 0 70.246 + && optionsMenu[i].valueNames[nameIdx].i == 0) { 70.247 + nameIdx = -1; 70.248 + } 70.249 + } 70.250 + 70.251 + if (nameIdx >= 0) { 70.252 + strncpy(value, optionsMenu[i].valueNames[nameIdx].c, 4); 70.253 + value[4] = 0; 70.254 + fprintf(out, "%s=%s\n", 70.255 + name, value); 70.256 + } else { 70.257 + fprintf(out, "%s=%d\n", 70.258 + name, n); 70.259 + } 70.260 +} 70.261 + 70.262 +void saveOptions(const struct LJPrefs *prefs) { 70.263 + FILE *out = ljfopen(ljIniName, "wt"); 70.264 + 70.265 + if (out) { 70.266 + for (int i = 0; i < PC_OPTIONS_MENU_LEN; ++i) { 70.267 + saveOption(i, prefs->number[i], out); 70.268 + } 70.269 + fprintf(out, "Skin=%s\n", 70.270 + skinName); 70.271 + fclose(out); 70.272 + } 70.273 +} 70.274 + 70.275 +void unpackOptions(LJView *v, const struct LJPrefs *prefs) { 70.276 + unpackCommonOptions(v, prefs->number); 70.277 + 70.278 + v->plat->nextAbove = prefs->number[OPTIONS_NEXT_ABOVE]; 70.279 + v->showTrails = prefs->number[OPTIONS_TRAILS]; 70.280 + autoPause = prefs->number[OPTIONS_AUTO_PAUSE]; 70.281 +} 70.282 + 70.283 +/******************************************************************** 70.284 + 70.285 +Allegro based option drawing code 70.286 + 70.287 +********************************************************************/ 70.288 + 70.289 +#define OPTIONS_TOP 100 70.290 +#define OPTIONS_ROW_HT 40 70.291 +#define OPTIONS_ROW_LEFT 80 70.292 +#define OPTIONS_ROW_MID 400 70.293 +#define OPTIONS_ROW_RIGHT 760 70.294 +#define OPTIONS_MENU_VIS 7 70.295 +#define OPTIONS_FONT aver32 70.296 + 70.297 + 70.298 +/** 70.299 + * @param y number of option to draw 70.300 + * @param hilite bitfield: 1=focused, 0=not 70.301 + */ 70.302 +void optionsDrawRow(const unsigned char *prefs, 70.303 + int dstY, int line, int value, int hilite) { 70.304 + unsigned int ht = text_height(OPTIONS_FONT); 70.305 + int buttonY = OPTIONS_TOP + OPTIONS_ROW_HT * dstY; 70.306 + int rowBg = bgColor; 70.307 + const char *nameText; 70.308 + char altNameText[8]; 70.309 + char valueText[OPTIONS_VALUE_LEN]; 70.310 + const char *valueOverride = isDisabledOption(prefs, line); 70.311 + int textcolor = fgColor; 70.312 + const char *valueDesc = NULL; 70.313 + 70.314 + { 70.315 + nameText = ljGetFourCCName(optionsMenu[line].name); 70.316 + if (!nameText) { 70.317 + strncpy(altNameText, optionsMenu[line].name.c, 4); 70.318 + altNameText[4] = 0; 70.319 + nameText = altNameText; 70.320 + } 70.321 + } 70.322 + 70.323 + if (valueOverride) { 70.324 + hilite |= 2; 70.325 + textcolor = makecol(128, 128, 128); 70.326 + } 70.327 + 70.328 + if (hilite == 3) { 70.329 + rowBg = makecol(204, 204, 204); 70.330 + } else if (hilite == 1) { 70.331 + rowBg = hiliteColor; 70.332 + } 70.333 + 70.334 + // If the value of this option is within range, format it as a string. 70.335 + if (value >= optionsMenu[line].minValue 70.336 + && value < optionsMenu[line].minValue + optionsMenu[line].nValues) { 70.337 + if (valueOverride) { 70.338 + ustrzcpy(valueText, sizeof(valueText), "overridden"); 70.339 + valueDesc = valueOverride; 70.340 + } else { 70.341 + valueDesc = getOptionsValueStr(valueText, line + OPTIONS_OFFSET, value); 70.342 + } 70.343 + } else { 70.344 + valueText[0] = '\0'; 70.345 + } 70.346 + 70.347 + // draw current option 70.348 + acquire_screen(); 70.349 + rectfill(screen, 70.350 + OPTIONS_ROW_LEFT, buttonY, 70.351 + OPTIONS_ROW_RIGHT - 1, buttonY + OPTIONS_ROW_HT - 1, 70.352 + rowBg); 70.353 + textout_ex(screen, OPTIONS_FONT, nameText, 70.354 + OPTIONS_ROW_LEFT + 8, buttonY + (OPTIONS_ROW_HT - ht) / 2, 70.355 + textcolor, rowBg); 70.356 + textout_ex(screen, OPTIONS_FONT, 70.357 + valueText, 70.358 + OPTIONS_ROW_MID, buttonY + (OPTIONS_ROW_HT - ht) / 2, 70.359 + textcolor, rowBg); 70.360 + 70.361 + // For an enabled selected item, draw the frame 70.362 + if (hilite == 1) { 70.363 + rect(screen, 70.364 + OPTIONS_ROW_LEFT, buttonY, 70.365 + OPTIONS_ROW_RIGHT - 1, buttonY + OPTIONS_ROW_HT - 1, 70.366 + fgColor); 70.367 + } 70.368 + 70.369 + // For a selected item, draw the help text 70.370 + if (hilite & 1) { 70.371 + buttonY = OPTIONS_TOP + OPTIONS_ROW_HT * OPTIONS_MENU_VIS; 70.372 + rectfill(screen, 70.373 + OPTIONS_ROW_LEFT, buttonY, 70.374 + OPTIONS_ROW_RIGHT - 1, buttonY + OPTIONS_ROW_HT * 5 / 2 - 1, 70.375 + bgColor); 70.376 + 70.377 + const char *descText = ljGetFourCCDesc(optionsMenu[line].name); 70.378 + if (descText) { 70.379 + textout_ex(screen, OPTIONS_FONT, descText, 70.380 + OPTIONS_ROW_LEFT, 70.381 + buttonY + OPTIONS_ROW_HT - ht / 2, 70.382 + textcolor, bgColor); 70.383 + } 70.384 + if (valueDesc) { 70.385 + textout_ex(screen, OPTIONS_FONT, valueDesc, 70.386 + OPTIONS_ROW_LEFT, 70.387 + buttonY + 2 * OPTIONS_ROW_HT - ht / 2, 70.388 + textcolor, bgColor); 70.389 + } 70.390 + } 70.391 + 70.392 + release_screen(); 70.393 +} 70.394 + 70.395 +void optionsClearRow(int dstY, int hilite) { 70.396 + int buttonY = OPTIONS_TOP + OPTIONS_ROW_HT * dstY; 70.397 + rectfill(screen, 70.398 + OPTIONS_ROW_LEFT, buttonY, 70.399 + OPTIONS_ROW_RIGHT - 1, buttonY + OPTIONS_ROW_HT * 5 / 2 - 1, 70.400 + bgColor); 70.401 +} 70.402 + 70.403 +void optionsDrawPage(int page, const unsigned char *prefs) { 70.404 + int nPages = 0; 70.405 + for (; optionsPages[nPages].name; ++nPages) { } 70.406 + 70.407 + vsync(); 70.408 + acquire_screen(); 70.409 + rectfill(screen, 320, 32, SCREEN_W - 1, 63, bgColor); 70.410 + textprintf_right_ex(screen, aver32, 70.411 + SCREEN_W - 16, 32, fgColor, -1, 70.412 + "(%d/%d) %s", 70.413 + page + 1, nPages, optionsPages[page].name); 70.414 + 70.415 + for (int i = optionsPages[page].start; 70.416 + i < optionsPages[page + 1].start; ++i) { 70.417 + optionsDrawRow(prefs, i - optionsPages[page].start, 70.418 + i, prefs[i], 0); 70.419 + } 70.420 + for (int i = optionsPages[page + 1].start; 70.421 + i < optionsPages[page].start + OPTIONS_MENU_VIS; ++i) { 70.422 + optionsClearRow(i - optionsPages[page].start, 0); 70.423 + } 70.424 + release_screen(); 70.425 +} 70.426 + 70.427 +void optionsWinInit(void) { 70.428 + acquire_screen(); 70.429 + clear_to_color(screen, bgColor); 70.430 + textout_ex(screen, aver32, "LOCKJAW > Options", 16, 32, fgColor, -1); 70.431 + textout_ex(screen, aver32, "Rotate: change page; Up/Down: select", 16, 522, fgColor, -1); 70.432 + textout_ex(screen, aver32, "Left/Right change; Enter: exit", 16, 552, fgColor, -1); 70.433 + release_screen(); 70.434 + 70.435 +} 70.436 + 70.437 +void optionsIdle(void) { 70.438 + rest(10); 70.439 +}
71.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 71.2 +++ b/src/options.c Fri Mar 13 00:39:12 2009 -0700 71.3 @@ -0,0 +1,642 @@ 71.4 +/* options code for LOCKJAW, an implementation of the Soviet Mind Game 71.5 + 71.6 +Copyright (C) 2007-2008 Damian Yerrick <tepples+lj@spamcop.net> 71.7 + 71.8 +This work is free software; you can redistribute it and/or modify 71.9 +it under the terms of the GNU General Public License as published by 71.10 +the Free Software Foundation; either version 2 of the License, or 71.11 +(at your option) any later version. 71.12 + 71.13 +This program is distributed in the hope that it will be useful, 71.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 71.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 71.16 +GNU General Public License for more details. 71.17 + 71.18 +You should have received a copy of the GNU General Public License 71.19 +along with this program; if not, write to the Free Software 71.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 71.21 + 71.22 +Original game concept and design by Alexey Pajitnov. 71.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 71.24 +or The Tetris Company LLC. 71.25 + 71.26 +*/ 71.27 + 71.28 + 71.29 +#include <string.h> 71.30 +#include <stdio.h> 71.31 +#include "options.h" 71.32 +#include "ljplay.h" 71.33 +#include "ljlocale.h" 71.34 + 71.35 +#ifdef HAS_FPU 71.36 +#define siprintf sprintf 71.37 +#define MIN_SHADOW 0 71.38 +#define N_SHADOWS LJSHADOW_N_STYLES 71.39 +#define FACTORY_SHADOW 0 71.40 +#include "ljpc.h" 71.41 +#else 71.42 +#define MIN_SHADOW LJSHADOW_COLORED 71.43 +#define N_SHADOWS (LJSHADOW_N_STYLES - 2) 71.44 +#define FACTORY_SHADOW MIN_SHADOW 71.45 +#endif 71.46 + 71.47 +const FourCC optionsBoolNames[2] = { 71.48 + {"Off"}, {"On"} 71.49 +}; 71.50 + 71.51 +const FourCC gimmickNames[LJGM_N_GIMMICKS] = { 71.52 + {"Mara"}, {"Line"}, {"Time"}, {"DrlA"}, {"Item"}, {"Keys"} 71.53 +}; 71.54 + 71.55 +const FourCC optionsGarbageNames[LJGARBAGE_N_STYLES] = { 71.56 + {"Off"}, {"Lv.1"}, {"Lv.2"}, {"Lv.3"}, {"Lv.4"}, 71.57 + {"HRD"}, {"DrlG"}, {"Zigz"} 71.58 +}; 71.59 + 71.60 +const FourCC optionsScoringNames[LJSCORE_N_STYLES] = { 71.61 + {"LJ"}, {"Fibo"}, {"HotL"}, {"TDSl"}, {"NESl"}, {"LJns"} 71.62 +}; 71.63 + 71.64 +const FourCC optionsKickLimitNames[N_KICK_LIMITS] = { 71.65 + {"Off"}, {"1"}, {"2"}, {"3"}, {"4"}, {"5"}, {"Inf"} 71.66 +}; 71.67 + 71.68 +const FourCC optionsSpeedCurveNames[] = { 71.69 + {"Zero"}, {"Rhy0"}, {"Exp"}, {"Rh20"}, 71.70 + {"TGM2"}, {"TAPD"}, {"TAD3"}, 71.71 + {"NESc"}, {"GBc"}, {"GBHL"} 71.72 +}; 71.73 + 71.74 +static const FourCC optionsWindowedNames[2] = { 71.75 + {"FulS"}, {"Wind"} 71.76 +}; 71.77 + 71.78 +const FourCC optionsShadowNames[LJSHADOW_N_STYLES] = { 71.79 + {"tl25"}, {"tl50"}, {"tlsC"}, {"tlsM"}, {"Off"}, {"invF"} 71.80 +}; 71.81 + 71.82 +const FourCC optionsLockDelayNames[2] = { 71.83 + {"SBSC"}, {"Inf"} 71.84 +}; 71.85 + 71.86 +const FourCC optionsSBSCNames[2] = { 71.87 + {"SBSC"}, {.i=0} 71.88 +}; 71.89 + 71.90 +const FourCC optionsTspinNames[] = { 71.91 + {"Off"}, {"Imob"}, {"CrnT"}, {"WKT"} 71.92 +}; 71.93 + 71.94 +const FourCC optionsLockdownNames[] = { 71.95 + {"OLD"}, {"EntR"}, {"StpR"}, {"MovR"} 71.96 +}; 71.97 + 71.98 +const FourCC optionsHoldStyleNames[] = { 71.99 + {"Off"}, {"HldE"}, {"HldR"}, {"HldN"} 71.100 +}; 71.101 + 71.102 +const FourCC optionsDropScoringNames[LJDROP_N_STYLES] = { 71.103 + {"None"}, {"ConD"}, {"S1H1"}, {"S1H2"} 71.104 +}; 71.105 + 71.106 +const FourCC optionsGravNames[] = { 71.107 + {"Naiv"}, {"Stky"}, {"byCo"}, {"Casc"} 71.108 +}; 71.109 + 71.110 +const FourCC optionsPieceSetNames[LJRAND_N_PIECE_SETS] = { 71.111 + {"IJLO"}, 71.112 + {"JLOT"}, 71.113 + {"SZSZ"}, 71.114 + {"IIII"}, 71.115 + {"AllP"}, 71.116 + {"TTTT"} 71.117 +}; 71.118 + 71.119 +const FourCC optionsRandNames[LJRAND_N_RANDS] = { 71.120 + {"Unif"}, 71.121 + {"Bag"}, 71.122 + {"Bag+"}, 71.123 + {"Bag2"}, 71.124 + {"Hist"}, 71.125 + {"His6"} 71.126 +}; 71.127 + 71.128 +const FourCC optionsLineDelayNames[] = { 71.129 + {"SBSC"}, {.i=0} 71.130 +}; 71.131 + 71.132 +const FourCC optionsZangiNames[] = { 71.133 + {"Slid"}, {"Lock"}, {"LocU"} 71.134 +}; 71.135 + 71.136 +const FourCC optionsGluingNames[] = { 71.137 + {"Off"}, {"Squ"}, {"Stky"}, {"byCo"} 71.138 +}; 71.139 + 71.140 +const FourCC optionsRotNames[N_ROTATION_SYSTEMS] = { 71.141 + {"SRS"}, {"Sega"}, {"ARS"}, {"Tngn"}, 71.142 + {"NRSR"}, {"NRSL"}, {"TOD4"}, {"TDX"} 71.143 +}; 71.144 + 71.145 + 71.146 +const OptionsLine commonOptionsMenu[] = { 71.147 + {{"gimm"}, gimmickNames, 71.148 + 0, LJGM_N_GIMMICKS, 0 }, 71.149 + {{"pfw"}, NULL, 71.150 + 4, LJ_PF_WID - 4 + 1, 10 }, 71.151 + {{"pfh"}, NULL, 71.152 + 8, LJ_PF_VIS_HT - 8 + 1, LJ_PF_VIS_HT }, 71.153 + {{"vzsp"}, optionsBoolNames, 71.154 + 0, 2, 1 }, 71.155 + {{"spdc"}, optionsSpeedCurveNames, 71.156 + 0, LJSPD_N_CURVES, LJSPD_EXP }, 71.157 + {{"are"}, NULL, 71.158 + 0, 121, 0, OPTSTYLE_FRAMES }, 71.159 + {{"piec"}, optionsPieceSetNames, 71.160 + 0, LJRAND_N_PIECE_SETS, LJRAND_4BLK }, 71.161 + {{"rand"}, optionsRandNames, 71.162 + 0, LJRAND_N_RANDS, LJRAND_BAG }, 71.163 + 71.164 + {{"hold"}, optionsHoldStyleNames, 71.165 + 0, LJHOLD_N_STYLES, LJHOLD_EMPTY }, 71.166 + {{"rots"}, optionsRotNames, 71.167 + 0, N_ROTATION_SYSTEMS, 0 }, 71.168 + {{"upkl"}, optionsKickLimitNames, 71.169 + 0, N_KICK_LIMITS, N_KICK_LIMITS - 1 }, 71.170 + {{"lock"}, optionsLockdownNames, 71.171 + 0, LJLOCK_N_STYLES, LJLOCK_MOVE }, 71.172 + {{"sldt"}, optionsLockDelayNames, 71.173 + 0, 129, 0, OPTSTYLE_FRAMES }, 71.174 + {{"deep"}, optionsBoolNames, 71.175 + 0, 2, 0 }, 71.176 + 71.177 + {{"clrd"}, optionsSBSCNames, 71.178 + 0, 121, 0, OPTSTYLE_FRAMES }, 71.179 + {{"grav"}, optionsGravNames, 71.180 + 0, LJGRAV_N_ALGOS, LJGRAV_NAIVE }, 71.181 + {{"glue"}, optionsGluingNames, 71.182 + 0, LJGLUING_N_STYLES, LJGLUING_NONE }, 71.183 + {{"lsco"}, optionsScoringNames, 71.184 + 0, LJSCORE_N_STYLES }, 71.185 + {{"dsco"}, optionsDropScoringNames, 71.186 + 0, LJDROP_N_STYLES, 0 }, 71.187 + {{"tspn"}, optionsTspinNames, 71.188 + 0, LJTS_N_ALGOS, LJTS_TDS }, 71.189 + {{"garb"}, optionsGarbageNames, 71.190 + 0, LJGARBAGE_N_STYLES, 0 }, 71.191 + 71.192 + {{"dasd"}, NULL, 71.193 + 1, 120, 10, OPTSTYLE_FRAMES }, 71.194 + {{"dass"}, NULL, 71.195 + 0, 10, 1, OPTSTYLE_FRAC_G }, 71.196 + {{"idas"}, optionsBoolNames, 71.197 + 0, 2, 1 }, 71.198 + {{"irs"}, optionsBoolNames, 71.199 + 0, 2, 1 }, 71.200 + {{"8way"}, optionsBoolNames, 71.201 + 0, 2, 0 }, 71.202 + 71.203 + {{"sfds"}, NULL, 71.204 + 1, 3, 1, OPTSTYLE_FRAC_G }, 71.205 + {{"sfdl"}, optionsZangiNames, 71.206 + 0, LJZANGI_N_STYLES, LJZANGI_SLIDE }, 71.207 + {{"hrdl"}, optionsZangiNames, 71.208 + 0, LJZANGI_N_STYLES, LJZANGI_LOCK }, 71.209 + 71.210 + {{"tls"}, optionsShadowNames + MIN_SHADOW, 71.211 + MIN_SHADOW, N_SHADOWS, FACTORY_SHADOW }, 71.212 + {{"invs"}, optionsBoolNames, 71.213 + 0, 2, 0 }, 71.214 + {{"next"}, NULL, 71.215 + 0, LJ_NEXT_PIECES + 1, 6 }, 71.216 + {{"srph"}, optionsBoolNames, 71.217 + 0, 2, 1 }, 71.218 + 71.219 +#ifdef HAS_FPU 71.220 + {{"inpv"}, NULL, 71.221 + 0, LJ_NEXT_PIECES + 1, 0 }, 71.222 + {{"mblr"}, optionsBoolNames, 71.223 + 0, 2, 1 }, 71.224 + {{"lidp"}, optionsBoolNames, 71.225 + 0, 2, 1 }, 71.226 + {{"rec"}, optionsBoolNames, 71.227 + 0, 2, 0 }, 71.228 + {{"wndw"}, optionsWindowedNames, 71.229 + 0, 2, 1 } 71.230 +#endif 71.231 +}; 71.232 + 71.233 +const OptionsPage optionsPages[] = { 71.234 + {OPTIONS_GIMMICK, "Game"}, 71.235 + {OPTIONS_WIDTH, "Rules: Well"}, 71.236 + {OPTIONS_HOLD_PIECE, "Rules: Movement"}, 71.237 + {OPTIONS_LINE_DELAY, "Rules: Line clear"}, 71.238 + {OPTIONS_SIDEWAYS_DELAY, "Control: Movement"}, 71.239 + {OPTIONS_SOFT_DROP_SPEED, "Control: Drop"}, 71.240 + {OPTIONS_SHADOW, "Display"}, 71.241 +#ifdef HAS_FPU 71.242 + {OPTIONS_MENU_LEN, "PC"}, 71.243 + {PC_OPTIONS_MENU_LEN, NULL} 71.244 +#else 71.245 + {OPTIONS_MENU_LEN, NULL} 71.246 +#endif 71.247 +}; 71.248 + 71.249 +void setOptionsValueToFourCC(char *dst, FourCC f) { 71.250 + const char *valueName = ljGetFourCCName(f); 71.251 + if (valueName) { 71.252 + strncpy(dst, 71.253 + valueName, 71.254 + OPTIONS_VALUE_LEN - 1); 71.255 + dst[OPTIONS_VALUE_LEN - 1] = 0; 71.256 + } else { 71.257 + strncpy(dst, f.c, 4); 71.258 + dst[4] = 0; 71.259 + } 71.260 +} 71.261 + 71.262 +struct DisabledOption { 71.263 + unsigned char name; 71.264 + unsigned char value; 71.265 + unsigned char name2; 71.266 + char reason[45]; 71.267 +}; 71.268 + 71.269 +/* 71.270 + Semantics: 71.271 + If option name is set to value, 71.272 + then gray out option name2 and draw its value as reason. 71.273 +*/ 71.274 +#define N_DISABLED_OPTIONS 12 71.275 +const struct DisabledOption disabledOptions[N_DISABLED_OPTIONS] = { 71.276 + { OPTIONS_LOCKDOWN, LJLOCK_NOW, 71.277 + OPTIONS_SOFT_DROP, "Lockdown is immediate" }, 71.278 + { OPTIONS_LOCKDOWN, LJLOCK_NOW, 71.279 + OPTIONS_HARD_DROP, "Lockdown is immediate" }, 71.280 + { OPTIONS_LOCKDOWN, LJLOCK_NOW, 71.281 + OPTIONS_LOCK_DELAY, "Lockdown is immediate" }, 71.282 + { OPTIONS_LOCK_DELAY, 128, 71.283 + OPTIONS_LOCKDOWN, "Lockdown is manual" }, 71.284 + { OPTIONS_SPEED_CURVE, LJSPD_DEATH, 71.285 + OPTIONS_SOFT_DROP_SPEED,"Death: pieces land instantly" }, 71.286 + { OPTIONS_SPEED_CURVE, LJSPD_RHYTHM, 71.287 + OPTIONS_SOFT_DROP_SPEED,"Rhythm: pieces land instantly" }, 71.288 + { OPTIONS_SPEED_CURVE, LJSPD_DEATH, 71.289 + OPTIONS_SMOOTH_GRAVITY, "Death: pieces land instantly" }, 71.290 + { OPTIONS_SPEED_CURVE, LJSPD_RHYTHM, 71.291 + OPTIONS_SMOOTH_GRAVITY, "Rhythm: pieces land instantly" }, 71.292 + { OPTIONS_SPEED_CURVE, LJSPD_DEATH, 71.293 + OPTIONS_SOFT_DROP, "Death: pieces land instantly" }, 71.294 + { OPTIONS_SPEED_CURVE, LJSPD_RHYTHM, 71.295 + OPTIONS_SOFT_DROP, "Rhythm: pieces land instantly" }, 71.296 + { OPTIONS_SPEED_CURVE, LJSPD_DEATH, 71.297 + OPTIONS_HARD_DROP, "Death: pieces land instantly" }, 71.298 + { OPTIONS_SPEED_CURVE, LJSPD_RHYTHM, 71.299 + OPTIONS_HARD_DROP, "Rhythm: pieces land instantly" }, 71.300 +}; 71.301 + 71.302 +const char *isDisabledOption(const unsigned char *prefs, int y) { 71.303 + for (int i = 0; i < N_DISABLED_OPTIONS; ++i) { 71.304 + if (y == disabledOptions[i].name2) { 71.305 + int name = disabledOptions[i].name; 71.306 + int value = disabledOptions[i].value; 71.307 + 71.308 + if (prefs[name] == value) { 71.309 + return disabledOptions[i].reason; 71.310 + } 71.311 + } 71.312 + } 71.313 + return NULL; 71.314 +} 71.315 + 71.316 + 71.317 +const char *getOptionsValueStr(char *dst, int line, int value) { 71.318 + const OptionsLine *l = &(commonOptionsMenu[line]); 71.319 + FourCC f = {.i = 0}; 71.320 + const char *desc = NULL; 71.321 + 71.322 + switch (l->style) { 71.323 + case OPTSTYLE_DEFAULT: 71.324 + if (l->valueNames) { 71.325 + f = l->valueNames[value - l->minValue]; 71.326 + } else { 71.327 + siprintf(dst, "%d", value); 71.328 + } 71.329 + break; 71.330 + 71.331 + case OPTSTYLE_FRAMES: 71.332 + if (l->valueNames 71.333 + && value == l->minValue 71.334 + && l->valueNames[0].i) { 71.335 + 71.336 + // override first with name 0 71.337 + f = l->valueNames[0]; 71.338 + } else if (l->valueNames 71.339 + && value == l->minValue 71.340 + + l->nValues - 1 71.341 + && l->valueNames[1].i) { 71.342 + 71.343 + // override second with name 1 71.344 + f = l->valueNames[1]; 71.345 + } else { 71.346 + if (value >= 60) { 71.347 + int ds = value / 6; 71.348 + int s = ds / 10; 71.349 + ds -= s * 10; 71.350 + siprintf(dst, "%d/60 s (%d.%d s)", value, s, ds); 71.351 + } else { 71.352 + int ms = value * 50 / 3; 71.353 + siprintf(dst, "%d/60 s (%d ms)", value, ms); 71.354 + } 71.355 + } break; 71.356 + 71.357 + case OPTSTYLE_FRAC_G: 71.358 + if (value > 6) { 71.359 + int dHz = 600 / value; 71.360 + int Hz = dHz / 10; 71.361 + dHz -= Hz * 10; 71.362 + siprintf(dst, "1/%dG (%d.%d Hz)", value, Hz, dHz); 71.363 + } else if (value > 0) { 71.364 + if (value > 1) { 71.365 + dst[0] = '1'; 71.366 + dst[1] = '/'; 71.367 + dst += 2; 71.368 + } 71.369 + siprintf(dst, "%dG (%d Hz)", value, 60 / value); 71.370 + } else { 71.371 + strcpy(dst, "Instant"); 71.372 + } 71.373 + break; 71.374 + 71.375 + default: 71.376 + strncpy(dst, "Unknown option style.", OPTIONS_VALUE_LEN - 1); 71.377 + dst[OPTIONS_VALUE_LEN - 1] = 0; 71.378 + break; 71.379 + } 71.380 + 71.381 + /* If we have a fourCC, use it. */ 71.382 + if (f.i != 0) { 71.383 + setOptionsValueToFourCC(dst, f); 71.384 + desc = ljGetFourCCDesc(f); 71.385 + } 71.386 + return desc; 71.387 +} 71.388 + 71.389 +void unpackCommonOptions(LJView *v, const unsigned char *prefs) { 71.390 + if (prefs[OPTIONS_GIMMICK] < 255) 71.391 + v->field->gimmick = prefs[OPTIONS_GIMMICK]; 71.392 + if (prefs[OPTIONS_WIDTH] < 255) { 71.393 + int width = prefs[OPTIONS_WIDTH]; 71.394 + v->field->leftWall = (LJ_PF_WID - width) / 2; 71.395 + v->field->rightWall = v->field->leftWall + width; 71.396 + } 71.397 + if (prefs[OPTIONS_HEIGHT] < 255) 71.398 + v->field->ceiling = prefs[OPTIONS_HEIGHT]; 71.399 + if (prefs[OPTIONS_ENTER_ABOVE] < 255) 71.400 + v->field->enterAbove = prefs[OPTIONS_ENTER_ABOVE]; 71.401 + if (prefs[OPTIONS_SPEED_CURVE] < 255) 71.402 + v->field->speedState.curve = prefs[OPTIONS_SPEED_CURVE]; 71.403 + if (prefs[OPTIONS_ENTRY_DELAY] < 255) 71.404 + v->field->areStyle = prefs[OPTIONS_ENTRY_DELAY]; 71.405 + if (prefs[OPTIONS_PIECE_SET] < 255) 71.406 + v->field->pieceSet = prefs[OPTIONS_PIECE_SET]; 71.407 + if (prefs[OPTIONS_RANDOMIZER] < 255) 71.408 + v->field->randomizer = prefs[OPTIONS_RANDOMIZER]; 71.409 + 71.410 + if (prefs[OPTIONS_ROTATION_SYSTEM] < 255) 71.411 + v->field->rotationSystem = prefs[OPTIONS_ROTATION_SYSTEM]; 71.412 + if (prefs[OPTIONS_FLOOR_KICKS] < 255) 71.413 + v->field->maxUpwardKicks = prefs[OPTIONS_FLOOR_KICKS] == N_KICK_LIMITS - 1 71.414 + ? 128 71.415 + : prefs[OPTIONS_FLOOR_KICKS]; 71.416 + if (prefs[OPTIONS_HOLD_PIECE] < 255) 71.417 + v->field->holdStyle = prefs[OPTIONS_HOLD_PIECE]; 71.418 + if (prefs[OPTIONS_LOCKDOWN] < 255) 71.419 + v->field->lockReset = prefs[OPTIONS_LOCKDOWN]; 71.420 + if (prefs[OPTIONS_LOCK_DELAY] < 255) 71.421 + v->field->setLockDelay = prefs[OPTIONS_LOCK_DELAY]; 71.422 + if (prefs[OPTIONS_BOTTOM_BLOCKS] < 255) 71.423 + v->field->bottomBlocks = prefs[OPTIONS_BOTTOM_BLOCKS]; 71.424 + 71.425 + if (prefs[OPTIONS_LINE_DELAY] < 255) 71.426 + v->field->setLineDelay = prefs[OPTIONS_LINE_DELAY]; 71.427 + if (prefs[OPTIONS_T_SPIN] < 255) 71.428 + v->field->tSpinAlgo = prefs[OPTIONS_T_SPIN]; 71.429 + if (prefs[OPTIONS_CLEAR_GRAVITY] < 255) 71.430 + v->field->clearGravity = prefs[OPTIONS_CLEAR_GRAVITY]; 71.431 + if (prefs[OPTIONS_GLUING] < 255) 71.432 + v->field->gluing = prefs[OPTIONS_GLUING]; 71.433 + if (prefs[OPTIONS_SCORING] < 255) 71.434 + v->field->scoreStyle = prefs[OPTIONS_SCORING]; 71.435 + if (prefs[OPTIONS_DROP_SCORING] < 255) 71.436 + v->field->dropScoreStyle = prefs[OPTIONS_DROP_SCORING]; 71.437 + if (prefs[OPTIONS_GARBAGE] < 255) 71.438 + v->field->garbageStyle = prefs[OPTIONS_GARBAGE]; 71.439 + 71.440 + if (prefs[OPTIONS_SIDEWAYS_DELAY] < 255) 71.441 + v->control->dasDelay = prefs[OPTIONS_SIDEWAYS_DELAY]; 71.442 + if (prefs[OPTIONS_SIDEWAYS_SPEED] < 255) 71.443 + v->control->dasSpeed = prefs[OPTIONS_SIDEWAYS_SPEED]; 71.444 + if (v->control->dasDelay < v->control->dasSpeed) { 71.445 + v->control->dasDelay = v->control->dasSpeed; 71.446 + } 71.447 + if (prefs[OPTIONS_INITIAL_SIDEWAYS] < 255) 71.448 + v->control->initialDAS = prefs[OPTIONS_INITIAL_SIDEWAYS]; 71.449 + if (prefs[OPTIONS_IRS] < 255) 71.450 + v->control->initialRotate = prefs[OPTIONS_IRS]; 71.451 + if (prefs[OPTIONS_DIAGONAL_MOTION] < 255) 71.452 + v->control->allowDiagonals = prefs[OPTIONS_DIAGONAL_MOTION]; 71.453 + if (prefs[OPTIONS_SOFT_DROP_SPEED] < 255) 71.454 + v->control->softDropSpeed = prefs[OPTIONS_SOFT_DROP_SPEED] - 1; 71.455 + if (prefs[OPTIONS_SOFT_DROP] < 255) 71.456 + v->control->softDropLock = prefs[OPTIONS_SOFT_DROP]; 71.457 + if (prefs[OPTIONS_HARD_DROP] < 255) 71.458 + v->control->hardDropLock = prefs[OPTIONS_HARD_DROP]; 71.459 + 71.460 + if (prefs[OPTIONS_SHADOW] < 255) 71.461 + v->hideShadow = prefs[OPTIONS_SHADOW]; 71.462 + if (prefs[OPTIONS_HIDE_PF] < 255) 71.463 + v->hidePF = prefs[OPTIONS_HIDE_PF]; 71.464 + if (prefs[OPTIONS_NEXT_PIECES] < 255) 71.465 + v->nextPieces = prefs[OPTIONS_NEXT_PIECES]; 71.466 + if (prefs[OPTIONS_SMOOTH_GRAVITY] < 255) 71.467 + v->smoothGravity = prefs[OPTIONS_SMOOTH_GRAVITY]; 71.468 +} 71.469 + 71.470 +void initOptions(unsigned char *prefs) { 71.471 + for (int i = 0; i < OPTIONS_MENU_LEN; ++i) { 71.472 + prefs[i] = commonOptionsMenu[i].startValue; 71.473 + } 71.474 +} 71.475 + 71.476 +LJBits menuReadPad(void); 71.477 +void vsync(void); 71.478 + 71.479 +void options(LJView *v, unsigned char *prefs) { 71.480 + int page = 0, y = 0; 71.481 + int redraw = 2; 71.482 + int done = 0; 71.483 + LJBits lastKeys = ~0; 71.484 + int dasDir = 0; 71.485 + int dasCounter = 0; 71.486 + const OptionsLine const *optionsMenu = commonOptionsMenu; 71.487 + int lastClock = getTime(); 71.488 + int erase = -1; 71.489 + 71.490 + optionsWinInit(); 71.491 + 71.492 + while (!done) { 71.493 + if (redraw) { 71.494 + if (redraw == 2) { 71.495 + redraw = 1; 71.496 + optionsDrawPage(page, prefs); 71.497 + } 71.498 + if (redraw == 1) { 71.499 + if (erase >= 0) { 71.500 + vsync(); 71.501 + optionsDrawRow(prefs, 71.502 + erase - optionsPages[page].start, 71.503 + erase, prefs[erase], 0); 71.504 + erase = -1; 71.505 + } 71.506 + optionsDrawRow(prefs, 71.507 + y - optionsPages[page].start, 71.508 + y, prefs[y], 1); 71.509 + redraw = 0; 71.510 + } 71.511 + } else { 71.512 + optionsIdle(); 71.513 + } 71.514 + 71.515 + LJBits keys = menuReadPad(); 71.516 + LJBits sounds = 0; 71.517 + int lastY = y; 71.518 + LJBits newKeys = keys & ~lastKeys; 71.519 + LJBits dasKeys = 0; 71.520 + 71.521 + if (getTime() != lastClock) { 71.522 + // Handle DAS within options (fixed at 250 ms 30 Hz) 71.523 + lastClock = getTime(); 71.524 + if (keys & dasDir) { 71.525 + ++dasCounter; 71.526 + if (dasCounter >= 15) { 71.527 + dasCounter -= 2; 71.528 + dasKeys = dasDir; 71.529 + } 71.530 + } else { 71.531 + dasCounter = 0; 71.532 + } 71.533 + } 71.534 + 71.535 + if (newKeys & VKEY_UP 71.536 + || ((dasKeys & VKEY_UP) 71.537 + && y > optionsPages[page].start)) { 71.538 + dasDir = VKEY_UP; 71.539 + if (y <= 0) { 71.540 + while (optionsPages[page].name) { 71.541 + ++page; 71.542 + } 71.543 + y = optionsPages[page].start - 1; 71.544 + } else { 71.545 + --y; 71.546 + } 71.547 + } 71.548 + if (newKeys & VKEY_DOWN 71.549 + || ((dasKeys & VKEY_DOWN) 71.550 + && y < optionsPages[page + 1].start - 1)) { 71.551 + dasDir = VKEY_DOWN; 71.552 + ++y; 71.553 + if (y >= optionsPages[page + 1].start 71.554 + && !optionsPages[page + 1].name) { 71.555 + y = 0; 71.556 + } 71.557 + } 71.558 + 71.559 + if (!isDisabledOption(prefs, y)) { 71.560 + if ((newKeys | dasKeys) & VKEY_RIGHT) { 71.561 + int num = prefs[y] + 1; 71.562 + 71.563 + if (num >= optionsMenu[y].minValue + optionsMenu[y].nValues) { 71.564 + prefs[y] = optionsMenu[y].minValue; 71.565 + } else { 71.566 + prefs[y] = num; 71.567 + } 71.568 + 71.569 + sounds |= LJSND_ROTATE; 71.570 + // XXX: need to redraw the whole box (redraw = 2) 71.571 + // if options have become enabled or disabled 71.572 + redraw = 1; 71.573 + dasDir = VKEY_RIGHT; 71.574 + } 71.575 + 71.576 + if ((newKeys | dasKeys) & VKEY_LEFT) { 71.577 + int num = prefs[y] - 1; 71.578 + 71.579 + if (num < optionsMenu[y].minValue) { 71.580 + prefs[y] = optionsMenu[y].minValue + optionsMenu[y].nValues - 1; 71.581 + } else { 71.582 + prefs[y] = num; 71.583 + } 71.584 + 71.585 + sounds |= LJSND_ROTATE; 71.586 + redraw = 1; 71.587 + dasDir = VKEY_LEFT; 71.588 + } 71.589 + } 71.590 + 71.591 + // Rotate left: Go to the top of the previous page if it exists. 71.592 + if (newKeys & VKEY_ROTL) { 71.593 + if (page > 0) { 71.594 + y = optionsPages[page - 1].start; 71.595 + } else { 71.596 + y = 0; 71.597 + } 71.598 + } 71.599 + 71.600 + // Rotate right: If on last page, finish; 71.601 + // otherwise, go to the top of the next page. 71.602 + if (newKeys & VKEY_ROTR) { 71.603 + if (!optionsPages[page + 1].name) { 71.604 + done = 1; 71.605 + } else { 71.606 + y = optionsPages[page + 1].start; 71.607 + } 71.608 + } 71.609 + 71.610 + // Start: finish 71.611 + if (newKeys & VKEY_START) { 71.612 + done = 1; 71.613 + } 71.614 + 71.615 + if (lastY != y) { 71.616 + sounds |= LJSND_SHIFT; 71.617 + 71.618 + // calculate which page the cursor has moved to 71.619 + int lastPage = page; 71.620 + while (y < optionsPages[page].start) { 71.621 + --page; 71.622 + } 71.623 + while (y >= optionsPages[page + 1].start) { 71.624 + ++page; 71.625 + } 71.626 + 71.627 + if (lastPage == page) { 71.628 + erase = lastY; 71.629 + if (redraw < 1) { 71.630 + redraw = 1; 71.631 + } 71.632 + } else { 71.633 + // turning the page 71.634 + sounds |= LJSND_HOLD; 71.635 + redraw = 2; // redraw the whole screen 71.636 + } 71.637 + } 71.638 + lastKeys = keys; 71.639 + 71.640 + if (done) { 71.641 + sounds |= LJSND_LINE; 71.642 + } 71.643 + playSoundEffects(v, sounds, 100); 71.644 + } 71.645 +}
72.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 72.2 +++ b/src/options.h Fri Mar 13 00:39:12 2009 -0700 72.3 @@ -0,0 +1,152 @@ 72.4 +/* options code for LOCKJAW, an implementation of the Soviet Mind Game 72.5 + 72.6 +Copyright (C) 2007 Damian Yerrick <tepples+lj@spamcop.net> 72.7 + 72.8 +This work is free software; you can redistribute it and/or modify 72.9 +it under the terms of the GNU General Public License as published by 72.10 +the Free Software Foundation; either version 2 of the License, or 72.11 +(at your option) any later version. 72.12 + 72.13 +This program is distributed in the hope that it will be useful, 72.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 72.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 72.16 +GNU General Public License for more details. 72.17 + 72.18 +You should have received a copy of the GNU General Public License 72.19 +along with this program; if not, write to the Free Software 72.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 72.21 + 72.22 +Original game concept and design by Alexey Pajitnov. 72.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 72.24 +or The Tetris Company LLC. 72.25 + 72.26 +*/ 72.27 + 72.28 +#ifndef OPTIONS_H 72.29 +#define OPTIONS_H 72.30 + 72.31 +#include "lj.h" 72.32 +#include "ljcontrol.h" 72.33 +#include "ljlocale.h" 72.34 + 72.35 +typedef struct OptionsLine { 72.36 + FourCC name; 72.37 + const FourCC *valueNames; 72.38 + unsigned char minValue; 72.39 + unsigned char nValues; 72.40 + unsigned char startValue; 72.41 + unsigned char style; 72.42 + // const char *desc; 72.43 +} OptionsLine; 72.44 + 72.45 +/* 72.46 + * OPTSTYLE_DEFAULT and no valueNames: 72.47 + * Render value as a number. 72.48 + * OPTSTYLE_DEFAULT and valueNames: 72.49 + * Render value as valueNames[value - minValue] 72.50 + * OPTSTYLE_FRAMES and no valueNames: 72.51 + * Render value as a number and as a number times 50/3. 72.52 + * OPTSTYLE_FRAMES and no valueNames: 72.53 + * Similar, except override value == minValue with valueNames[0] 72.54 + * and value == minValue + nValues - 1 with valueNames[1]. 72.55 + */ 72.56 +enum { 72.57 + OPTSTYLE_DEFAULT, // names, or numbers 72.58 + OPTSTYLE_FRAMES, 72.59 + OPTSTYLE_FRAC_G 72.60 +}; 72.61 + 72.62 +typedef struct OptionsPage { 72.63 + unsigned int start; 72.64 + const char *name; 72.65 +} OptionsPage; 72.66 + 72.67 +#define N_KICK_LIMITS 7 72.68 +extern const OptionsLine commonOptionsMenu[]; 72.69 +extern const OptionsPage optionsPages[]; 72.70 +extern const FourCC optionsRotNames[N_ROTATION_SYSTEMS]; 72.71 +extern const FourCC optionsLockdownNames[]; 72.72 +extern const FourCC gimmickNames[LJGM_N_GIMMICKS]; 72.73 +extern const FourCC optionsPieceSetNames[]; 72.74 +extern const FourCC optionsBoolNames[2]; 72.75 +extern const FourCC optionsZangiNames[]; 72.76 +extern const FourCC optionsGluingNames[]; 72.77 +extern const FourCC optionsRandNames[]; 72.78 +extern const FourCC optionsTspinNames[LJTS_N_ALGOS]; 72.79 +extern const FourCC optionsGravNames[]; 72.80 +extern const FourCC optionsSpeedCurveNames[]; 72.81 +extern const FourCC optionsGarbageNames[]; 72.82 +extern const FourCC optionsHoldStyleNames[]; 72.83 +extern const FourCC optionsScoringNames[LJSCORE_N_STYLES]; 72.84 +extern const FourCC optionsDropScoringNames[LJDROP_N_STYLES]; 72.85 +extern const FourCC gimmickNames[LJGM_N_GIMMICKS]; 72.86 +extern const FourCC optionsSideNames[]; 72.87 +extern const FourCC optionsNextStyleNames[]; 72.88 +extern const FourCC optionsShadowNames[]; 72.89 +extern const FourCC optionsDASDelayNames[]; 72.90 + 72.91 + 72.92 +enum { 72.93 + OPTIONS_GIMMICK, 72.94 + 72.95 + OPTIONS_WIDTH, 72.96 + OPTIONS_HEIGHT, 72.97 + OPTIONS_ENTER_ABOVE, 72.98 + OPTIONS_SPEED_CURVE, 72.99 + OPTIONS_ENTRY_DELAY, 72.100 + OPTIONS_PIECE_SET, 72.101 + OPTIONS_RANDOMIZER, 72.102 + 72.103 + OPTIONS_HOLD_PIECE, 72.104 + OPTIONS_ROTATION_SYSTEM, 72.105 + OPTIONS_FLOOR_KICKS, 72.106 + OPTIONS_LOCKDOWN, 72.107 + OPTIONS_LOCK_DELAY, 72.108 + OPTIONS_BOTTOM_BLOCKS, 72.109 + 72.110 + OPTIONS_LINE_DELAY, 72.111 + OPTIONS_CLEAR_GRAVITY, 72.112 + OPTIONS_GLUING, 72.113 + OPTIONS_SCORING, 72.114 + OPTIONS_DROP_SCORING, 72.115 + OPTIONS_T_SPIN, 72.116 + OPTIONS_GARBAGE, 72.117 + 72.118 + OPTIONS_SIDEWAYS_DELAY, 72.119 + OPTIONS_SIDEWAYS_SPEED, 72.120 + OPTIONS_INITIAL_SIDEWAYS, 72.121 + OPTIONS_IRS, 72.122 + OPTIONS_DIAGONAL_MOTION, 72.123 + OPTIONS_SOFT_DROP_SPEED, 72.124 + OPTIONS_SOFT_DROP, 72.125 + OPTIONS_HARD_DROP, 72.126 + 72.127 + OPTIONS_SHADOW, 72.128 + OPTIONS_HIDE_PF, 72.129 + OPTIONS_NEXT_PIECES, 72.130 + OPTIONS_SMOOTH_GRAVITY, 72.131 + 72.132 + OPTIONS_MENU_LEN 72.133 +}; 72.134 + 72.135 +/** 72.136 + * Builds a string representing the value of an option. 72.137 + * @param dst 32-byte buffer to hold this string 72.138 + * @param line index into optionsMenu 72.139 + * @param value the (numeric) value of the option 72.140 + * @return the description of this value, or NULL if unknown 72.141 + */ 72.142 +const char *getOptionsValueStr(char *dst, int line, int value); 72.143 +#define OPTIONS_VALUE_LEN 33 72.144 + 72.145 +void unpackCommonOptions(LJView *v, const unsigned char *prefs); 72.146 +void initOptions(unsigned char *prefs); 72.147 +const char *isDisabledOption(const unsigned char *prefs, int y); 72.148 + 72.149 +void optionsWinInit(void); 72.150 +void optionsDrawPage(int scroll, const unsigned char *prefs); 72.151 +void optionsDrawRow(const unsigned char *prefs, 72.152 + int dstY, int line, int value, int hilite); 72.153 +void optionsIdle(void); 72.154 + 72.155 +#endif
73.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 73.2 +++ b/src/pcdebrief.c Fri Mar 13 00:39:12 2009 -0700 73.3 @@ -0,0 +1,72 @@ 73.4 +#include "ljpc.h" 73.5 + 73.6 +void debriefDrawPage(const char *page, size_t pageNumber) { 73.7 + int y = 40; 73.8 + char line[256]; 73.9 + int linePos = 0; 73.10 + int done = 0; 73.11 + 73.12 + acquire_screen(); 73.13 + clear_to_color(screen, bgColor); 73.14 + textout_centre_ex(screen, aver32, "GAME OVER", 73.15 + SCREEN_W / 2, y, fgColor, -1); 73.16 + textprintf_right_ex(screen, aver32, 73.17 + SCREEN_W - 40, y, fgColor, -1, 73.18 + "Page %u", (unsigned int)pageNumber + 1); 73.19 + textout_centre_ex(screen, aver32, 73.20 + "Left/Right: Change; Rotate: close", 73.21 + SCREEN_W / 2, SCREEN_H - 40, fgColor, -1); 73.22 + 73.23 + while (!done) { 73.24 + int c = *page++; 73.25 + 73.26 + // Break at newline and at end of text 73.27 + if (c == '\n' || c == 0) { 73.28 + 73.29 + // Draw blank and parenthetical lines in smaller font 73.30 + const FONT *f = (linePos == 0 || line[0] == '(') 73.31 + ? aver16 73.32 + : aver32; 73.33 + int lineH = text_height(f); 73.34 + 73.35 + // Terminate the line of text and print it 73.36 + line[linePos] = 0; 73.37 + textout_ex(screen, f, line, 40, y, fgColor, -1); 73.38 + 73.39 + linePos = 0; // Carriage return 73.40 + y += lineH * 6 / 5; // Line feed 73.41 + } else { 73.42 + if (linePos + 2 < sizeof(line)) { 73.43 + line[linePos++] = c; 73.44 + } 73.45 + } 73.46 + if (c == 0) { 73.47 + done = 1; 73.48 + } 73.49 + } 73.50 + release_screen(); 73.51 +} 73.52 + 73.53 +extern volatile char redrawWholeScreen; 73.54 + 73.55 +LJBits debriefHandleKeys(void) { 73.56 + int keys = menuReadPad(); 73.57 + 73.58 + while (keypressed()) { 73.59 + int scancode; 73.60 + ureadkey(&scancode); 73.61 + 73.62 + if (scancode == KEY_PRTSCR) { 73.63 + saveScreen(-1); 73.64 + } 73.65 + } 73.66 + 73.67 + if (wantsClose) { 73.68 + keys |= VKEY_ROTL; 73.69 + } 73.70 + 73.71 + if (!(keys & (VKEY_ROTL | VKEY_ROTR))) { 73.72 + rest(30); 73.73 + } 73.74 + return keys; 73.75 +}
74.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 74.2 +++ b/src/pcjoy.c Fri Mar 13 00:39:12 2009 -0700 74.3 @@ -0,0 +1,503 @@ 74.4 +/* PC joystick code 74.5 + 74.6 +Copyright (C) 2006 Damian Yerrick <tepples+lj@spamcop.net> 74.7 + 74.8 +This work is free software; you can redistribute it and/or modify 74.9 +it under the terms of the GNU General Public License as published by 74.10 +the Free Software Foundation; either version 2 of the License, or 74.11 +(at your option) any later version. 74.12 + 74.13 +This program is distributed in the hope that it will be useful, 74.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 74.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 74.16 +GNU General Public License for more details. 74.17 + 74.18 +You should have received a copy of the GNU General Public License 74.19 +along with this program; if not, write to the Free Software 74.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 74.21 + 74.22 +Original game concept and design by Alexey Pajitnov. 74.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 74.24 +or The Tetris Company LLC. 74.25 + 74.26 +*/ 74.27 + 74.28 +#include "pcjoy.h" 74.29 +#include <allegro.h> 74.30 +#include <string.h> 74.31 +#include "ljpath.h" 74.32 + 74.33 +extern const FONT *aver32, *aver16; 74.34 +extern int bgColor, fgColor, hiliteColor; 74.35 + 74.36 +static volatile int lastScancodePressed = -1; 74.37 +static void (*oldKeyListener)(int scancode); 74.38 +volatile int wantsClose = 0; 74.39 +void ezPlaySample(const char *filename, int vol); 74.40 + 74.41 +static void ljpcKeyListener(int scancode) { 74.42 + if (!(scancode & 0x80)) { 74.43 + lastScancodePressed = scancode; 74.44 + } 74.45 + if (oldKeyListener) { 74.46 + oldKeyListener(scancode); 74.47 + } 74.48 +} END_OF_FUNCTION(ljpcKeyListener); 74.49 + 74.50 + 74.51 +/** 74.52 + * Presses the escape key when the user clicks the close box. 74.53 + */ 74.54 +static void setWantsClose() { 74.55 + wantsClose = 1; 74.56 +} 74.57 + 74.58 +static int withJoystick = 0; 74.59 + 74.60 +#define N_VKEYS 14 74.61 +#define N_PLAYERS 2 74.62 + 74.63 +static const char keysFileName[] = "lj-keys.043"; 74.64 + 74.65 +static const struct pkeyMapping defaultKeymappings[N_PLAYERS][N_VKEYS] = { 74.66 + { 74.67 + // [0]: player 1 74.68 + {-1, -1, KEY_UP}, 74.69 + {-1, -1, KEY_DOWN}, 74.70 + {-1, -1, KEY_LEFT}, 74.71 + {-1, -1, KEY_RIGHT}, 74.72 + {-1, -1, KEY_Z}, 74.73 + {-1, -1, KEY_X}, 74.74 + {-1, -1, KEY_S}, 74.75 + {-1, -1, KEY_SPACE}, 74.76 + {-1, -1, KEY_C}, 74.77 + {-1, -1, KEY_W}, 74.78 + {-1, -1, KEY_Q}, 74.79 + {-1, -1, KEY_E}, 74.80 + {-1, -1, KEY_ENTER}, 74.81 + {-1, -1, KEY_D} 74.82 + }, 74.83 + { 74.84 + // [1]: player 2 74.85 + {-1, -1, KEY_UP}, 74.86 + {-1, -1, KEY_DOWN}, 74.87 + {-1, -1, KEY_LEFT}, 74.88 + {-1, -1, KEY_RIGHT}, 74.89 + {-1, -1, KEY_Z}, 74.90 + {-1, -1, KEY_X}, 74.91 + {-1, -1, KEY_S}, 74.92 + {-1, -1, KEY_SPACE}, 74.93 + {-1, -1, KEY_C}, 74.94 + {-1, -1, KEY_W}, 74.95 + {-1, -1, KEY_Q}, 74.96 + {-1, -1, KEY_E}, 74.97 + {-1, -1, KEY_ENTER}, 74.98 + {-1, -1, KEY_D} 74.99 + } 74.100 +}; 74.101 + 74.102 +static struct pkeyMapping keymappings[N_PLAYERS][N_VKEYS]; 74.103 + 74.104 +void getPkeyName(char *dst, int j, int s, int a) { 74.105 + if (j < 0) { 74.106 + if (a >= 0 && a < KEY_MAX) { 74.107 + usprintf(dst, "Key %d (%s)", a, scancode_to_name(a)); 74.108 + } else { 74.109 + usprintf(dst, "Key %d (???"")", a); 74.110 + } 74.111 + } else if (s < 0) { 74.112 + usprintf(dst, 74.113 + "Joy %d button %s", 74.114 + j, 74.115 + joy[j].button[a].name); 74.116 + } else if (a < 0) { 74.117 + usprintf(dst, 74.118 + "Joy %d stick %s axis %s -", 74.119 + j, 74.120 + joy[j].stick[s].name, 74.121 + joy[j].stick[s].axis[~a].name); 74.122 + } else { 74.123 + usprintf(dst, 74.124 + "Joy %d stick %s axis %s +", 74.125 + j, 74.126 + joy[j].stick[s].name, 74.127 + joy[j].stick[s].axis[a].name); 74.128 + } 74.129 +} 74.130 + 74.131 +static int getPkeyState(int j, int s, int a) { 74.132 + int k; 74.133 + if (j < 0) { 74.134 + k = key[a]; 74.135 + } else if (s < 0) { 74.136 + k = joy[j].button[a].b; 74.137 + } else if (a < 0) { 74.138 + k = joy[j].stick[s].axis[~a].d1; 74.139 + } else { 74.140 + k = joy[j].stick[s].axis[a].d2; 74.141 + } 74.142 + return k; 74.143 +} 74.144 + 74.145 +const char *const vkeyNames[] = { 74.146 + "Hard Drop (Up)", 74.147 + "Soft Drop (Down)", 74.148 + "Left", 74.149 + "Right", 74.150 + "Rotate Left", 74.151 + "Rotate Right", 74.152 + "Hold", 74.153 + "Item (unused)", 74.154 + "Alt. Rotate Left", 74.155 + "Rotate Left Twice", 74.156 + "Far Left", 74.157 + "Far Right", 74.158 + "Alt. Firm Drop", 74.159 + "Alt. Hold", 74.160 + "Macro G", 74.161 + "Macro H" 74.162 +}; 74.163 + 74.164 +static int getVkeyState(int player, int vkey) { 74.165 + int j = keymappings[player][vkey].joy; 74.166 + int s = keymappings[player][vkey].stick; 74.167 + int a = keymappings[player][vkey].axis; 74.168 + 74.169 + return getPkeyState(j, s, a); 74.170 +} 74.171 + 74.172 +LJBits readPad(unsigned int player) { 74.173 + int keys = 0; 74.174 + poll_joystick(); 74.175 + 74.176 + for (int i = 0; 74.177 + i < N_VKEYS; 74.178 + ++i) { 74.179 + if (getVkeyState(player, i)) { 74.180 + keys |= 1 << i; 74.181 + } 74.182 + } 74.183 + return keys; 74.184 +} 74.185 + 74.186 +LJBits menuReadPad(void) { 74.187 + if (key[KEY_ENTER]) { 74.188 + return VKEY_ROTR | VKEY_START; 74.189 + } else if (key[KEY_ESC]) { 74.190 + return VKEY_ROTL; 74.191 + } else if (key[KEY_UP]) { 74.192 + return VKEY_UP; 74.193 + } else if (key[KEY_DOWN]) { 74.194 + return VKEY_DOWN; 74.195 + } else if (key[KEY_LEFT]) { 74.196 + return VKEY_LEFT; 74.197 + } else if (key[KEY_RIGHT]) { 74.198 + return VKEY_RIGHT; 74.199 + } else { 74.200 + return readPad(0) | readPad(1); 74.201 + } 74.202 +} 74.203 + 74.204 +// These contain the states of ALL buttons on ALL 74.205 +// joysticks 74.206 +static LJBits lastConfigButtons[8]; 74.207 +static LJBits lastConfigStickAxis[8]; 74.208 + 74.209 +static int newButton(int *outJ, int *outS, int *outA) { 74.210 + poll_joystick(); 74.211 + int found = 0; 74.212 + 74.213 + if (lastScancodePressed >= 0) { 74.214 + *outJ = -1; 74.215 + *outS = -1; 74.216 + *outA = lastScancodePressed; 74.217 + lastScancodePressed = -1; 74.218 + return 1; 74.219 + } 74.220 + 74.221 + for (int j = 0; 74.222 + j < num_joysticks; 74.223 + ++j) { 74.224 + LJBits cur = 0; 74.225 + 74.226 + for (int b = 0; b < joy[j].num_buttons; ++b) { 74.227 + if (joy[j].button[b].b) { 74.228 + if (!(lastConfigButtons[j] & (1 << b)) && !found) { 74.229 + *outJ = j; 74.230 + *outS = -1; 74.231 + *outA = b; 74.232 + found = 1; 74.233 + } 74.234 + cur |= 1 << b; 74.235 + } 74.236 + } 74.237 + lastConfigButtons[j] = cur; 74.238 + } 74.239 + if (found) { 74.240 + return 1; 74.241 + } 74.242 + 74.243 + for (int j = 0; 74.244 + j < num_joysticks; 74.245 + ++j) { 74.246 + LJBits cur = 0; 74.247 + LJBits mask = 1; 74.248 + 74.249 + for (int s = 0; s < joy[j].num_sticks; ++s) { 74.250 + for (int a = 0; a < joy[j].stick[s].num_axis; ++a) { 74.251 + if (joy[j].stick[s].axis[a].d1) { 74.252 + if (!(lastConfigStickAxis[j] & mask) && !found) { 74.253 + *outJ = j; 74.254 + *outS = s; 74.255 + *outA = ~a; 74.256 + found = 1; 74.257 + } 74.258 + cur |= mask; 74.259 + } 74.260 + mask <<= 1; 74.261 + if (joy[j].stick[s].axis[a].d2) { 74.262 + if (!(lastConfigStickAxis[j] & mask) && !found) { 74.263 + *outJ = j; 74.264 + *outS = s; 74.265 + *outA = a; 74.266 + found = 1; 74.267 + } 74.268 + cur |= mask; 74.269 + } 74.270 + mask <<= 1; 74.271 + } 74.272 + } 74.273 + lastConfigStickAxis[j] = cur; 74.274 + } 74.275 + return found; 74.276 +} 74.277 + 74.278 +static void clearNewButton(void) { 74.279 + int j, s, a; 74.280 + while (newButton(&j, &s, &a)); 74.281 +} 74.282 + 74.283 +void loadKeys(const char *filename) { 74.284 + FILE *fp = ljfopen(filename, "rb"); 74.285 + 74.286 + memcpy(keymappings, defaultKeymappings, sizeof(keymappings)); 74.287 + if (fp) { 74.288 + for (unsigned int player = 0; player < 2; ++player) { 74.289 + for (unsigned int vkey = 0; 74.290 + vkey < N_VKEYS; 74.291 + ++vkey) { 74.292 + int j = fgetc(fp); 74.293 + int s = fgetc(fp); 74.294 + int a = fgetc(fp); 74.295 + 74.296 + if (a == EOF) { 74.297 + break; 74.298 + } 74.299 + keymappings[player][vkey].joy = j; 74.300 + keymappings[player][vkey].stick = s; 74.301 + keymappings[player][vkey].axis = a; 74.302 + } 74.303 + } 74.304 + fclose(fp); 74.305 + } 74.306 +} 74.307 + 74.308 +void saveKeys(const char *filename) { 74.309 + FILE *fp = ljfopen(filename, "wb"); 74.310 + 74.311 + if (fp) { 74.312 + for (unsigned int player = 0; player < 2; ++player) { 74.313 + for (unsigned int vkey = 0; 74.314 + vkey < N_VKEYS && !feof(fp); 74.315 + ++vkey) { 74.316 + fputc(keymappings[player][vkey].joy, fp); 74.317 + fputc(keymappings[player][vkey].stick, fp); 74.318 + fputc(keymappings[player][vkey].axis, fp); 74.319 + } 74.320 + } 74.321 + fclose(fp); 74.322 + } 74.323 +} 74.324 + 74.325 +#define VKEY_ROWHT 24 74.326 +#define VKEY_TOP 120 74.327 + 74.328 +void drawVkeyRow(int vkey, int hilite) { 74.329 + char name[256]; 74.330 + int y = VKEY_TOP + vkey * VKEY_ROWHT; 74.331 + 74.332 + rectfill(screen, 74.333 + 16, y, 719, y + VKEY_ROWHT - 1, 74.334 + hilite ? hiliteColor : bgColor); 74.335 + textout_ex(screen, aver16, vkeyNames[vkey], 24, y + 4, fgColor, -1); 74.336 + for (int player = 0; player < N_PLAYERS; ++player) { 74.337 + if (player + 1 == hilite) { 74.338 + rect(screen, 74.339 + 240 + 240 * player, y, 74.340 + 479 + 240 * player, y + VKEY_ROWHT - 1, 74.341 + fgColor); 74.342 + } 74.343 + getPkeyName(name, 74.344 + keymappings[player][vkey].joy, 74.345 + keymappings[player][vkey].stick, 74.346 + keymappings[player][vkey].axis); 74.347 + textout_ex(screen, aver16, name, 240 + 240 * player, y + 4, fgColor, -1); 74.348 + } 74.349 +} 74.350 + 74.351 +/** 74.352 + * Waits for a key or button press. If any key but Esc is pressed, 74.353 + * reassigns the vkey. Otherwise, does nothing. 74.354 + * @param vkey the index of the vkey to reassign 74.355 + * @return 0 if key not changed; nonzero if key was changed 74.356 + */ 74.357 +static int changeKey(int player, int vkey) { 74.358 + int changed = 0; 74.359 + int phase = 0; 74.360 + 74.361 + clearNewButton(); 74.362 + while (!changed) { 74.363 + int j, s, a; 74.364 + 74.365 + if (phase == 5) { 74.366 + drawVkeyRow(vkey, 0); 74.367 + } else if (phase == 0) { 74.368 + drawVkeyRow(vkey, player + 1); 74.369 + phase = 15; 74.370 + } 74.371 + --phase; 74.372 + 74.373 + if (keypressed()) { 74.374 + int scancode; 74.375 + ureadkey(&scancode); 74.376 + if (scancode == KEY_ESC) { 74.377 + changed = -1; 74.378 + } 74.379 + } 74.380 + if (vkey < N_VKEYS && newButton(&j, &s, &a)) { 74.381 + if (j >= 0 || s > 0 || a != KEY_ESC) { 74.382 + keymappings[player][vkey].joy = j; 74.383 + keymappings[player][vkey].stick = s; 74.384 + keymappings[player][vkey].axis = a; 74.385 + ezPlaySample("nextS_wav", 128); 74.386 + changed = 1; 74.387 + } else { 74.388 + changed = -1; 74.389 + } 74.390 + } 74.391 + if (wantsClose) { 74.392 + changed = -1; 74.393 + } 74.394 + 74.395 + rest(30); 74.396 + } 74.397 + 74.398 + // Draw new vkey value 74.399 + drawVkeyRow(vkey, 0); 74.400 + return changed > 0; 74.401 +} 74.402 + 74.403 +void configureKeys(void) { 74.404 + clear_to_color(screen, bgColor); 74.405 + textout_ex(screen, aver32, "LOCKJAW > Game Keys", 16, 32, fgColor, -1); 74.406 + textout_ex(screen, aver16, 74.407 + "Use arrow keys to select a key. To reassign the selected key, press Enter", 74.408 + 40, 80, fgColor, -1); 74.409 + textout_ex(screen, aver16, 74.410 + "and then the key you want to use. When done, press Esc.", 74.411 + 40, 96, fgColor, -1); 74.412 + 74.413 + // Draw each vkey's name 74.414 + for (int vkey = 0; vkey < N_VKEYS; ++vkey) { 74.415 + drawVkeyRow(vkey, 0); 74.416 + } 74.417 + 74.418 + clearNewButton(); 74.419 + int vkey = 0; 74.420 + int player = 0; 74.421 + 74.422 + drawVkeyRow(vkey, player + 1); 74.423 + 74.424 + while (vkey >= 0 && !wantsClose) { 74.425 + int scancode = 0; 74.426 + 74.427 + rest(30); 74.428 + if (keypressed()) { 74.429 + ureadkey(&scancode); 74.430 + } 74.431 + 74.432 + switch (scancode) { 74.433 + 74.434 + case KEY_RIGHT: 74.435 + if (player < N_PLAYERS - 1) { 74.436 + ezPlaySample("shift_wav", 128); 74.437 + drawVkeyRow(vkey, 0); 74.438 + ++player; 74.439 + drawVkeyRow(vkey, player + 1); 74.440 + } 74.441 + break; 74.442 + 74.443 + case KEY_LEFT: 74.444 + if (player > 0) { 74.445 + ezPlaySample("shift_wav", 128); 74.446 + drawVkeyRow(vkey, 0); 74.447 + --player; 74.448 + drawVkeyRow(vkey, player + 1); 74.449 + } 74.450 + break; 74.451 + 74.452 + case KEY_UP: 74.453 + if (vkey > 0) { 74.454 + ezPlaySample("shift_wav", 128); 74.455 + drawVkeyRow(vkey, 0); 74.456 + --vkey; 74.457 + drawVkeyRow(vkey, player + 1); 74.458 + } 74.459 + break; 74.460 + case KEY_DOWN: 74.461 + if (vkey < N_VKEYS - 1) { 74.462 + ezPlaySample("shift_wav", 128); 74.463 + drawVkeyRow(vkey, 0); 74.464 + ++vkey; 74.465 + drawVkeyRow(vkey, player + 1); 74.466 + } 74.467 + break; 74.468 + case KEY_ENTER: 74.469 + ezPlaySample("rotate_wav", 96); 74.470 + while (vkey < N_VKEYS && changeKey(player, vkey)) { 74.471 + rest(150); 74.472 + ++vkey; 74.473 + } 74.474 + if (vkey >= N_VKEYS) { 74.475 + ezPlaySample("land_wav", 128); 74.476 + vkey = N_VKEYS - 1; 74.477 + } else { 74.478 + ezPlaySample("nextO_wav", 128); 74.479 + } 74.480 + drawVkeyRow(vkey, player + 1); 74.481 + break; 74.482 + case KEY_ESC: 74.483 + vkey = -1; 74.484 + break; 74.485 + } 74.486 + } 74.487 + saveKeys(keysFileName); 74.488 + ezPlaySample("line_wav", 128); 74.489 +} 74.490 + 74.491 + 74.492 +void initKeys(void) { 74.493 + LOCK_FUNCTION(ljpcKeyListener); 74.494 + LOCK_VARIABLE(lastScancodePressed); 74.495 + LOCK_VARIABLE(oldKeyListener); 74.496 + LOCK_FUNCTION(setWantsClose); 74.497 + LOCK_VARIABLE(wantsClose); 74.498 + 74.499 + oldKeyListener = keyboard_lowlevel_callback; 74.500 + keyboard_lowlevel_callback = ljpcKeyListener; 74.501 + withJoystick = !install_joystick(JOY_TYPE_AUTODETECT); 74.502 + loadKeys(keysFileName); 74.503 + set_close_button_callback(setWantsClose); 74.504 +} 74.505 + 74.506 +
75.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 75.2 +++ b/src/pcjoy.h Fri Mar 13 00:39:12 2009 -0700 75.3 @@ -0,0 +1,70 @@ 75.4 +/* PC joystick code 75.5 + 75.6 +Copyright (C) 2006 Damian Yerrick <tepples+lj@spamcop.net> 75.7 + 75.8 +This work is free software; you can redistribute it and/or modify 75.9 +it under the terms of the GNU General Public License as published by 75.10 +the Free Software Foundation; either version 2 of the License, or 75.11 +(at your option) any later version. 75.12 + 75.13 +This program is distributed in the hope that it will be useful, 75.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 75.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 75.16 +GNU General Public License for more details. 75.17 + 75.18 +You should have received a copy of the GNU General Public License 75.19 +along with this program; if not, write to the Free Software 75.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 75.21 + 75.22 +Original game concept and design by Alexey Pajitnov. 75.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 75.24 +or The Tetris Company LLC. 75.25 + 75.26 +*/ 75.27 + 75.28 +#ifndef PCJOY_H 75.29 +#define PCJOY_H 75.30 + 75.31 +#include "ljtypes.h" 75.32 +#include "ljcontrol.h" 75.33 + 75.34 + 75.35 +/* Physical key definitions 75.36 + 75.37 +m.joy < 0: 75.38 + key[m.axis] 75.39 +m.joy >= 0, m.stick < 0: 75.40 + joy[m.joy].button[m.axis].b 75.41 +m.joy >= 0, m.stick >= 0, m.axis < 0: 75.42 + joy[m.joy].stick[m.stick].axis[~m.axis].d1 75.43 +m.joy >= 0, m.stick >= 0, m.axis >= 0: 75.44 + joy[m.joy].stick[m.stick].axis[m.axis].d2 75.45 +*/ 75.46 + 75.47 +struct pkeyMapping { 75.48 + signed char joy; 75.49 + signed char stick; 75.50 + signed char axis; 75.51 +}; 75.52 + 75.53 +extern volatile int wantsClose; 75.54 + 75.55 +void initKeys(void); 75.56 + 75.57 +/** 75.58 + * Reads the physical keys to produce a set of pressed virtual keys. 75.59 + */ 75.60 +LJBits readPad(unsigned int player); 75.61 + 75.62 +/** 75.63 + * Reads the physical keys to produce a set of pressed virtual keys, 75.64 + * hardcoding keyboard Up, Down, Left, Right, Esc, and Enter 75.65 + * to the appropriate vkeys. 75.66 + */ 75.67 +LJBits menuReadPad(void); 75.68 + 75.69 +void getPkeyName(char *dst, int j, int s, int a); 75.70 +extern const char *const vkeyNames[]; 75.71 +void configureKeys(void); 75.72 + 75.73 +#endif
76.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 76.2 +++ b/src/pcsound.c Fri Mar 13 00:39:12 2009 -0700 76.3 @@ -0,0 +1,108 @@ 76.4 +#include "ljpc.h" 76.5 +extern const DATAFILE *sound_dat; 76.6 +extern char withSound; 76.7 + 76.8 +static const unsigned short shiftPitches[16] = { 76.9 + 630, 667, 707, 749, 794, 841, 891, 944, 76.10 + 1000, 1059, 1122, 1189, 1260, 1335, 1424, 1498 76.11 +}; 76.12 + 76.13 +/** 76.14 + * Plays a sample from datafile by name, with the specified volume 76.15 + * and the specified rate scale factor, panned center, without loop. 76.16 + * @param filename name of object in datafile 76.17 + * @param vol scale factor for volume (0-255); if set to 0, 76.18 + * stops all voices playing the sample 76.19 + * @param pitch scale factor for playback rate (1000 = normal; 2000 = double speed) 76.20 + */ 76.21 +static void playPitchSample(const char *filename, int vol, int pitch) { 76.22 + if (withSound && sound_dat) { 76.23 + const DATAFILE *entry = find_datafile_object(sound_dat, filename); 76.24 + 76.25 + if (entry) { 76.26 + if (vol < 1) { 76.27 + stop_sample(entry->dat); 76.28 + } else { 76.29 + play_sample(entry->dat, vol, 128, pitch, 0); 76.30 + } 76.31 + } 76.32 + } 76.33 +} 76.34 + 76.35 +void ezPlaySample(const char *filename, int vol) { 76.36 + playPitchSample(filename, vol, 1000); 76.37 +} 76.38 + 76.39 +void playSampleForTetromino(int piece) { 76.40 + static const char tetrominoNames[] = "IJLOSTZ22V"; 76.41 + 76.42 + piece &= LJP_MASK; 76.43 + if (piece >= 0 && piece < 10) { 76.44 + char filename[32]; 76.45 + usprintf(filename, "next%c_wav", tetrominoNames[piece]); 76.46 + ezPlaySample(filename, 128); 76.47 + } 76.48 +} 76.49 + 76.50 +void playSoundEffects(LJView *v, LJBits sounds, int countdown) { 76.51 + // Handle sound 76.52 + if ((sounds & LJSND_SPAWN) 76.53 + && !(v->hideNext)) { 76.54 + playSampleForTetromino(v->field->curPiece[1]); 76.55 + } 76.56 + if (sounds & LJSND_HOLD) { 76.57 + ezPlaySample("hold_wav", 128); 76.58 + } 76.59 + if (sounds & LJSND_ROTATE) { 76.60 + ezPlaySample("rotate_wav", 128); 76.61 + } 76.62 + if (sounds & LJSND_IRS) { 76.63 + ezPlaySample("irs_wav", 128); 76.64 + } 76.65 + if (sounds & LJSND_SQUARE) { 76.66 + ezPlaySample("square_wav", 128); 76.67 + } 76.68 + if (sounds & LJSND_SHIFT) { 76.69 + int x = v->plat->skin->shiftScale 76.70 + ? v->field->x + 8 - (LJ_PF_WID - 4) / 2 76.71 + : 8; 76.72 + int pitch = shiftPitches[x]; 76.73 + playPitchSample("shift_wav", 128, pitch); 76.74 + } 76.75 + if (sounds & LJSND_LAND) { 76.76 + ezPlaySample("land_wav", 192); 76.77 + } 76.78 + if (sounds & LJSND_LOCK) { 76.79 + ezPlaySample("lock_wav", 192); 76.80 + } 76.81 + if (sounds & LJSND_LINE) { 76.82 + ezPlaySample("line_wav", 128); 76.83 + } 76.84 + if (sounds & LJSND_SECTIONUP) { 76.85 + ezPlaySample("sectionup_wav", 128); 76.86 + } 76.87 + 76.88 + if (v->plat->b2bcd1 > 0) { 76.89 + if (--v->plat->b2bcd1 == 0) { 76.90 + playPitchSample("line_wav", 148, 1200); 76.91 + } 76.92 + } 76.93 + if (sounds & LJSND_SETB2B) { 76.94 + v->plat->b2bcd1 = 10; 76.95 + } 76.96 + if (v->plat->b2bcd2 > 0) { 76.97 + if (--v->plat->b2bcd2 == 0) { 76.98 + playPitchSample("line_wav", 168, 1440); 76.99 + } 76.100 + } 76.101 + if (sounds & LJSND_B2B) { 76.102 + v->plat->b2bcd2 = 20; 76.103 + } 76.104 + if (countdown < v->control->countdown) { 76.105 + char name[32]; 76.106 + usprintf(name, "count_%d_wav", countdown); 76.107 + ezPlaySample(name, 128); 76.108 + } 76.109 + LJMusic_poll(v->plat->skin->bgm); 76.110 +} 76.111 +
77.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 77.2 +++ b/src/pin8gba_sound.h Fri Mar 13 00:39:12 2009 -0700 77.3 @@ -0,0 +1,117 @@ 77.4 +/* 77.5 + * pin8gba_sound.h 77.6 + * Header file for GBA sound registers 77.7 + */ 77.8 + 77.9 + 77.10 +/* Copyright 2001-2006 Damian Yerrick 77.11 + 77.12 +(insert zlib license here) 77.13 + 77.14 +*/ 77.15 + 77.16 + 77.17 +/* Why the funny register names? 77.18 + 77.19 +The register names do not match the names in the official GBA 77.20 +documentation. I choose the names that seem most logical to me. 77.21 +In addition, I do slick macro tricks with the VRAM addresses for 77.22 +maps and the like and with registers that control timers, DMA, 77.23 +and backgrounds. The gba.h that comes with wintermute's libgba 77.24 +incorporates some but not all of these tricks. In addition, 77.25 +the names in gba.h for the sound registers aren't descriptive 77.26 +at all. 77.27 + 77.28 +*/ 77.29 + 77.30 +#ifndef PIN8GBA_SOUND_H 77.31 +#ifdef __cplusplus 77.32 +extern "C" { 77.33 +#endif 77.34 +#define PIN8GBA_SOUND_H 77.35 + 77.36 + 77.37 +#define DMGSNDCTRL (*(volatile u16 *)0x04000080) 77.38 +#define DMGSNDCTRL_LVOL(x) (x) 77.39 +#define DMGSNDCTRL_RVOL(x) ((x) << 4) 77.40 +#define DMGSNDCTRL_LSQR1 0x0100 77.41 +#define DMGSNDCTRL_LSQR2 0x0200 77.42 +#define DMGSNDCTRL_LTRI 0x0400 77.43 +#define DMGSNDCTRL_LNOISE 0x0800 77.44 +#define DMGSNDCTRL_RSQR1 0x1000 77.45 +#define DMGSNDCTRL_RSQR2 0x2000 77.46 +#define DMGSNDCTRL_RTRI 0x4000 77.47 +#define DMGSNDCTRL_RNOISE 0x8000 77.48 + 77.49 +#define DSOUNDCTRL (*(volatile u16 *)0x04000082) 77.50 +#define DSOUNDCTRL_DMG25 0x0000 77.51 +#define DSOUNDCTRL_DMG50 0x0001 77.52 +#define DSOUNDCTRL_DMG100 0x0002 77.53 +#define DSOUNDCTRL_A50 0x0000 77.54 +#define DSOUNDCTRL_A100 0x0004 77.55 +#define DSOUNDCTRL_B50 0x0000 77.56 +#define DSOUNDCTRL_B100 0x0008 77.57 +#define DSOUNDCTRL_AR 0x0100 77.58 +#define DSOUNDCTRL_AL 0x0200 77.59 +#define DSOUNDCTRL_ATIMER(x) ((x) << 10) 77.60 +#define DSOUNDCTRL_ARESET 0x0400 77.61 +#define DSOUNDCTRL_BR 0x1000 77.62 +#define DSOUNDCTRL_BL 0x2000 77.63 +#define DSOUNDCTRL_BTIMER(x) ((x) << 14) 77.64 +#define DSOUNDCTRL_BRESET 0x8000 77.65 + 77.66 +#define SNDSTAT (*(volatile u16*)0x04000084) 77.67 +#define SNDSTAT_SQR1 0x0001 77.68 +#define SNDSTAT_SQR2 0x0002 77.69 +#define SNDSTAT_TRI 0x0004 77.70 +#define SNDSTAT_NOISE 0x0008 77.71 +#define SNDSTAT_ENABLE 0x0080 77.72 + 77.73 +#define SNDBIAS (*(volatile u16 *)0x04000088) 77.74 +#define SETSNDRES(x) SNDBIAS = (SNDBIAS & 0x3fff) | (x << 14) 77.75 + 77.76 +#define DSOUND_FIFOA (*(volatile u32 *)0x040000a0) 77.77 +#define DSOUND_FIFOB (*(volatile u32 *)0x040000a4) 77.78 + 77.79 + 77.80 +#define SQR1SWEEP (*(volatile u16 *)0x04000060) 77.81 +#define SQR1SWEEP_OFF 0x0008 77.82 + 77.83 +#define SQR1CTRL (*(volatile u16 *)0x04000062) 77.84 +#define SQR2CTRL (*(volatile u16 *)0x04000068) 77.85 +#define NOISECTRL (*(volatile u16 *)0x04000078) 77.86 +#define SQR_DUTY(n) ((n) << 6) 77.87 +#define SQR_VOL(n) ((n) << 12) 77.88 + 77.89 +#define SQR1FREQ (*(volatile u16 *)0x04000064) 77.90 +#define SQR2FREQ (*(volatile u16 *)0x0400006c) 77.91 +#define TRIFREQ (*(volatile u16 *)0x04000074) 77.92 +#define FREQ_HOLD 0x0000 77.93 +#define FREQ_TIMED 0x4000 77.94 +#define FREQ_RESET 0x8000 77.95 + 77.96 +#define NOISEFREQ (*(volatile u16 *)0x0400007c) 77.97 +#define NOISEFREQ_127 0x0008 77.98 +#define NOISEFREQ_OCT(x) ((x) << 4) 77.99 + 77.100 +#define TRICTRL (*(volatile u16 *)0x04000070) 77.101 +#define TRICTRL_2X32 0x0000 77.102 +#define TRICTRL_1X64 0x0020 77.103 +#define TRICTRL_BANK(x) ((x) << 6) 77.104 +#define TRICTRL_ENABLE 0x0080 77.105 + 77.106 +#define TRILENVOL (*(volatile u16 *)0x04000072) 77.107 +#define TRILENVOL_LEN(x) (256 - (x)) 77.108 +#define TRILENVOL_MUTE 0x0000 77.109 +#define TRILENVOL_25 0x6000 77.110 +#define TRILENVOL_50 0x4000 77.111 +#define TRILENVOL_75 0x8000 77.112 +#define TRILENVOL_100 0x2000 77.113 + 77.114 +#define TRIWAVERAM ((volatile u32 *)0x04000090) 77.115 + 77.116 + 77.117 +#ifdef __cplusplus 77.118 +} 77.119 +#endif 77.120 +#endif
78.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 78.2 +++ b/src/random.c Fri Mar 13 00:39:12 2009 -0700 78.3 @@ -0,0 +1,253 @@ 78.4 +/* Piece randomizers for LOCKJAW, an implementation of the Soviet Mind Game 78.5 + 78.6 +Copyright (C) 2006-2007 Damian Yerrick <tepples+lj@spamcop.net> 78.7 + 78.8 +This work is free software; you can redistribute it and/or modify 78.9 +it under the terms of the GNU General Public License as published by 78.10 +the Free Software Foundation; either version 2 of the License, or 78.11 +(at your option) any later version. 78.12 + 78.13 +This program is distributed in the hope that it will be useful, 78.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 78.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 78.16 +GNU General Public License for more details. 78.17 + 78.18 +You should have received a copy of the GNU General Public License 78.19 +along with this program; if not, write to the Free Software 78.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 78.21 + 78.22 +Original game concept and design by Alexey Pajitnov. 78.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 78.24 +or The Tetris Company LLC. 78.25 + 78.26 +*/ 78.27 + 78.28 +#include "lj.h" 78.29 + 78.30 +// Order matters. The history randomizers prefer to give out the 78.31 +// front half early in the game. You don't want to give an S or Z 78.32 +// first, or you force a slide. You also don't want to give an O 78.33 +// first, or you force a slide with OS or OZ. 78.34 + 78.35 +static const signed char piecesNoI[] = { 78.36 + 1, 2, 5, // front half: J L T 78.37 + 3, 4, 6 // back half: O S Z 78.38 +}; 78.39 + 78.40 +static const signed char piecesTetrominoes[] = { 78.41 + 0, 1, 2, 5, // front half: I J L T 78.42 + 3, 4, 6 // back half: O S Z 78.43 +}; 78.44 + 78.45 +static const signed char piecesWithSmall[] = { 78.46 + 0, 1, 2, 5, 9, // front half: I J L T L3 78.47 + 3, 7, 8, 4, 6 // back half: O I2 I3 S Z 78.48 + // make sure that the S and Z are in the back half 78.49 +}; 78.50 + 78.51 +static const signed char piecesSZ[] = { 78.52 + 4, 6 78.53 +}; 78.54 + 78.55 +static const signed char piecesI[] = { 78.56 + 0 78.57 +}; 78.58 + 78.59 +static const signed char piecesT[] = { 78.60 + 5 78.61 +}; 78.62 + 78.63 +static const signed char *const pieceSets[] = { 78.64 + piecesTetrominoes, 78.65 + piecesNoI, 78.66 + piecesSZ, 78.67 + piecesI, 78.68 + piecesWithSmall, 78.69 + piecesT 78.70 +}; 78.71 + 78.72 +static const unsigned char pieceSetSizes[] = { 78.73 + sizeof(piecesTetrominoes), 78.74 + sizeof(piecesNoI), 78.75 + sizeof(piecesSZ), 78.76 + sizeof(piecesI), 78.77 + sizeof(piecesWithSmall), 78.78 + sizeof(piecesT) 78.79 +}; 78.80 + 78.81 +static void bagInitRandomize(LJField *p) { 78.82 + p->permuPhase = 0; 78.83 +} 78.84 + 78.85 +static unsigned int bagRandomize(LJField *p) { 78.86 + unsigned int setNo = p->pieceSet; 78.87 + unsigned int len = pieceSetSizes[setNo]; 78.88 + const signed char *set = pieceSets[setNo]; 78.89 + 78.90 + int piece; 78.91 + 78.92 + // If we're out of permutation pieces, make new ones by copying 78.93 + // from one of the rand#Bag arrays until we hit the -1 sentinel. 78.94 + if (p->permuPhase == 0) { 78.95 + int pos; 78.96 + 78.97 + for (pos = 0; pos < len; ++pos) { 78.98 + p->permuPiece[pos] = set[pos]; 78.99 + p->permuPiece[pos + len] = set[pos]; 78.100 + } 78.101 + 78.102 + // Double bag: Add one randomly chosen piece to the bag 78.103 + if (p->randomizer == LJRAND_2BAG) { 78.104 + pos *= 2; 78.105 + } 78.106 + // 7+1 Bag (TOJ style): Add one randomly chosen piece to the bag 78.107 + else if (p->randomizer == LJRAND_BAGPLUS1) { 78.108 + p->permuPiece[pos++] = set[ljRand(p) % len]; 78.109 + } 78.110 + p->permuPhase = pos; 78.111 + } 78.112 + 78.113 + // Choose a position in the remainder of the deck 78.114 + int r = (p->permuPhase > 1) ? ljRand(p) % p->permuPhase : 0; 78.115 + 78.116 + // Swap the top card with the card at this position 78.117 + piece = p->permuPiece[r]; 78.118 + p->permuPiece[r] = p->permuPiece[--p->permuPhase]; 78.119 + return piece; 78.120 +} 78.121 + 78.122 +static void mtbInitRandomize(LJField *p) { 78.123 + unsigned int setNo = p->pieceSet; 78.124 + unsigned int len = pieceSetSizes[setNo]; 78.125 + const signed char *set = pieceSets[setNo]; 78.126 + 78.127 + // At game start, front three are I, J, L 78.128 + // Back four (never dealt as first tetromino) are O, S, T, Z 78.129 + for (int i = 0; i < len; ++i) { 78.130 + p->permuPiece[i] = set[i]; 78.131 + } 78.132 +} 78.133 + 78.134 +static unsigned int mtbRandomize(LJField *p) { 78.135 + unsigned int setNo = p->pieceSet; 78.136 + unsigned int len = pieceSetSizes[setNo]; 78.137 + 78.138 + // Choose a piece from the three in front 78.139 + int r = ljRand(p) % (len / 2); 78.140 + int piece = p->permuPiece[r]; 78.141 + 78.142 + // Move it to the back 78.143 + for (; r < len - 1; ++r) { 78.144 + p->permuPiece[r] = p->permuPiece[r + 1]; 78.145 + } 78.146 + p->permuPiece[len - 1] = piece; 78.147 + return piece; 78.148 +} 78.149 + 78.150 +static void tgmInitRandomize(LJField *p) { 78.151 + unsigned int setNo = p->pieceSet; 78.152 + unsigned int len = pieceSetSizes[setNo]; 78.153 + 78.154 + p->permuPiece[0] = 0; 78.155 + for (int i = len / 2; i < len; ++i) { 78.156 + p->permuPiece[i] = i & 1 ? LJP_Z : LJP_S; 78.157 + } 78.158 +} 78.159 + 78.160 +/* State of TGM randomizer: 78.161 + * permuPiece[3..6]: history, [3] most recent 78.162 + * permuPiece[2]: 0 for first piece (use equal probability I, J, L, T) 78.163 + * or 1 for subsequent pieces 78.164 + */ 78.165 +static unsigned int tgmRandomize(LJField *p) { 78.166 + unsigned int setNo = p->pieceSet; 78.167 + unsigned int len = pieceSetSizes[setNo]; 78.168 + const signed char *set = pieceSets[setNo]; 78.169 + 78.170 + int r = ljRand(p) ^ (ljRand(p) << 15); 78.171 + int piece; 78.172 + 78.173 + if (p->permuPiece[0]) { 78.174 + 78.175 + // Roll up to 6 times for pieces 78.176 + for (int rolls = 6; 78.177 + rolls > 0; 78.178 + --rolls, r /= len) { 78.179 + piece = set[r % len]; 78.180 + int found = 0; 78.181 + 78.182 + // If the piece is not in the history, use it now 78.183 + for (int histoPos = len / 2; 78.184 + !found && histoPos < len; 78.185 + ++histoPos) { 78.186 + if (piece == p->permuPiece[histoPos]) { 78.187 + found = 1; 78.188 + } 78.189 + } 78.190 + if (!found) { 78.191 + break; 78.192 + } 78.193 + } 78.194 + } else { 78.195 + p->permuPiece[0] = 1; 78.196 + // Generate only pieces in the first half of the list 78.197 + piece = set[r % ((len + 1) / 2)]; 78.198 + if (piece == LJP_O) { 78.199 + piece = LJP_T; 78.200 + } 78.201 + } 78.202 + 78.203 + // Move it to the back 78.204 + for (r = len / 2; r < len - 1; ++r) { 78.205 + p->permuPiece[r] = p->permuPiece[r + 1]; 78.206 + } 78.207 + p->permuPiece[len - 1] = piece; 78.208 + return piece; 78.209 +} 78.210 + 78.211 +void initRandomize(LJField *p) { 78.212 + unsigned int setNo = p->pieceSet; 78.213 + unsigned int len = pieceSetSizes[setNo]; 78.214 + 78.215 + if (len < 2) { 78.216 + p->randomizer = LJRAND_PURE; 78.217 + } 78.218 + 78.219 + switch (p->randomizer) { 78.220 + case LJRAND_PURE: 78.221 + break; 78.222 + case LJRAND_BAG: 78.223 + case LJRAND_BAGPLUS1: 78.224 + case LJRAND_2BAG: 78.225 + bagInitRandomize(p); 78.226 + break; 78.227 + case LJRAND_HIST_INF: 78.228 + mtbInitRandomize(p); 78.229 + break; 78.230 + case LJRAND_HIST_6: 78.231 + tgmInitRandomize(p); 78.232 + break; 78.233 + } 78.234 +} 78.235 + 78.236 +unsigned int randomize(LJField *p) { 78.237 + switch (p->randomizer) { 78.238 + 78.239 + case LJRAND_BAG: 78.240 + case LJRAND_BAGPLUS1: 78.241 + case LJRAND_2BAG: 78.242 + return bagRandomize(p); 78.243 + case LJRAND_HIST_INF: 78.244 + return mtbRandomize(p); 78.245 + case LJRAND_HIST_6: 78.246 + return tgmRandomize(p); 78.247 + case LJRAND_PURE: 78.248 + default: 78.249 + { 78.250 + unsigned int setNo = p->pieceSet; 78.251 + unsigned int len = pieceSetSizes[setNo]; 78.252 + const signed char *set = pieceSets[setNo]; 78.253 + return set[ljRand(p) % len]; 78.254 + } 78.255 + } 78.256 +}
79.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 79.2 +++ b/src/scenario.c Fri Mar 13 00:39:12 2009 -0700 79.3 @@ -0,0 +1,339 @@ 79.4 +/* Scenario code for LOCKJAW, an implementation of the Soviet Mind Game 79.5 + 79.6 +Copyright (C) 2008 Damian Yerrick <tepples+lj@spamcop.net> 79.7 + 79.8 +This work is free software; you can redistribute it and/or modify 79.9 +it under the terms of the GNU General Public License as published by 79.10 +the Free Software Foundation; either version 2 of the License, or 79.11 +(at your option) any later version. 79.12 + 79.13 +This program is distributed in the hope that it will be useful, 79.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 79.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 79.16 +GNU General Public License for more details. 79.17 + 79.18 +You should have received a copy of the GNU General Public License 79.19 +along with this program; if not, write to the Free Software 79.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 79.21 + 79.22 +Original game concept and design by Alexey Pajitnov. 79.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 79.24 +or The Tetris Company LLC. 79.25 + 79.26 +*/ 79.27 + 79.28 +#include "scenario.h" 79.29 +#include "options.h" 79.30 +#include <string.h> 79.31 + 79.32 +#define END_OF_PRESET {OPTIONS_MENU_LEN} 79.33 + 79.34 +const Preset predefPresets[] = { 79.35 + {"Guideline", { 79.36 + {OPTIONS_WIDTH, 10}, 79.37 + {OPTIONS_HEIGHT, 20}, 79.38 + {OPTIONS_ENTRY_DELAY, 0}, 79.39 + {OPTIONS_PIECE_SET, LJRAND_4BLK}, 79.40 + {OPTIONS_RANDOMIZER, LJRAND_BAG}, 79.41 + {OPTIONS_HOLD_PIECE, LJHOLD_EMPTY}, 79.42 + {OPTIONS_ROTATION_SYSTEM, LJROT_SRS}, 79.43 + {OPTIONS_FLOOR_KICKS, 6}, 79.44 + {OPTIONS_LOCKDOWN, LJLOCK_MOVE}, 79.45 + {OPTIONS_LOCK_DELAY, 30}, 79.46 + {OPTIONS_BOTTOM_BLOCKS, 0}, 79.47 + {OPTIONS_LINE_DELAY, 30}, 79.48 + {OPTIONS_CLEAR_GRAVITY, LJGRAV_NAIVE}, 79.49 + {OPTIONS_GLUING, 0}, 79.50 + {OPTIONS_SCORING, LJSCORE_TDS}, 79.51 + {OPTIONS_DROP_SCORING, LJDROP_1S_2H}, 79.52 + {OPTIONS_T_SPIN, LJTS_TDS}, 79.53 + END_OF_PRESET 79.54 + }}, 79.55 + 79.56 + {"Classic", { 79.57 + {OPTIONS_GIMMICK, LJGM_ATYPE}, 79.58 + {OPTIONS_WIDTH, 10}, 79.59 + {OPTIONS_HEIGHT, 20}, 79.60 + {OPTIONS_ENTER_ABOVE, 0}, 79.61 + {OPTIONS_ENTRY_DELAY, 10}, 79.62 + {OPTIONS_SPEED_CURVE, LJSPD_NES}, 79.63 + {OPTIONS_PIECE_SET, LJRAND_4BLK}, 79.64 + {OPTIONS_RANDOMIZER, LJRAND_PURE}, 79.65 + {OPTIONS_HOLD_PIECE, LJHOLD_NONE}, 79.66 + {OPTIONS_ROTATION_SYSTEM, LJROT_NES}, 79.67 + {OPTIONS_FLOOR_KICKS, 0}, 79.68 + {OPTIONS_LOCKDOWN, LJLOCK_NOW}, 79.69 + {OPTIONS_LOCK_DELAY, 0}, 79.70 + {OPTIONS_BOTTOM_BLOCKS, 0}, 79.71 + {OPTIONS_LINE_DELAY, 30}, 79.72 + {OPTIONS_CLEAR_GRAVITY, LJGRAV_NAIVE}, 79.73 + {OPTIONS_GLUING, 0}, 79.74 + {OPTIONS_SCORING, LJSCORE_NES}, 79.75 + {OPTIONS_DROP_SCORING, LJDROP_NES}, 79.76 + {OPTIONS_GARBAGE, LJGARBAGE_NONE}, 79.77 + {OPTIONS_SIDEWAYS_DELAY, 15}, 79.78 + {OPTIONS_SIDEWAYS_SPEED, 6}, 79.79 + {OPTIONS_SOFT_DROP_SPEED, 2}, 79.80 + {OPTIONS_INITIAL_SIDEWAYS, 1}, 79.81 + {OPTIONS_IRS, 0}, 79.82 + {OPTIONS_NEXT_PIECES, 1}, 79.83 + END_OF_PRESET 79.84 + }}, 79.85 + 79.86 + /* http://www.tetrisconcept.com/forum/viewtopic.php?t=892 */ 79.87 + {"Master", { 79.88 + {OPTIONS_GIMMICK, LJGM_ATYPE}, 79.89 + {OPTIONS_WIDTH, 10}, 79.90 + {OPTIONS_HEIGHT, 20}, 79.91 + {OPTIONS_ENTER_ABOVE, 0}, 79.92 + {OPTIONS_SPEED_CURVE, LJSPD_TGM}, 79.93 + {OPTIONS_ENTRY_DELAY, 30}, 79.94 + {OPTIONS_PIECE_SET, LJRAND_4BLK}, 79.95 + {OPTIONS_RANDOMIZER, LJRAND_HIST_6}, 79.96 + {OPTIONS_HOLD_PIECE, LJHOLD_NONE}, 79.97 + {OPTIONS_ROTATION_SYSTEM, LJROT_ARIKA}, 79.98 + {OPTIONS_FLOOR_KICKS, 0}, 79.99 + {OPTIONS_LOCKDOWN, LJLOCK_STEP}, 79.100 + {OPTIONS_LOCK_DELAY, 30}, 79.101 + {OPTIONS_BOTTOM_BLOCKS, 0}, 79.102 + {OPTIONS_LINE_DELAY, 0}, 79.103 + {OPTIONS_CLEAR_GRAVITY, LJGRAV_NAIVE}, 79.104 + {OPTIONS_GLUING, LJGLUING_STICKY}, 79.105 + {OPTIONS_GARBAGE, LJGARBAGE_NONE}, 79.106 + {OPTIONS_SIDEWAYS_DELAY, 16}, 79.107 + {OPTIONS_SIDEWAYS_SPEED, 1}, 79.108 + {OPTIONS_INITIAL_SIDEWAYS, 0}, 79.109 + {OPTIONS_IRS, 1}, 79.110 + {OPTIONS_SOFT_DROP_SPEED, 1}, 79.111 + {OPTIONS_SOFT_DROP, LJZANGI_LOCK}, 79.112 + {OPTIONS_HARD_DROP, LJZANGI_SLIDE}, 79.113 + {OPTIONS_NEXT_PIECES, 1}, 79.114 + END_OF_PRESET 79.115 + }}, 79.116 + 79.117 + /* http://www.tetrisconcept.com/forum/viewtopic.php?t=892 */ 79.118 + {"T.A. Death", { 79.119 + {OPTIONS_GIMMICK, LJGM_ATYPE}, 79.120 + {OPTIONS_SPEED_CURVE, LJSPD_DEATH}, 79.121 + {OPTIONS_WIDTH, 10}, 79.122 + {OPTIONS_HEIGHT, 20}, 79.123 + {OPTIONS_ENTER_ABOVE, 0}, 79.124 + {OPTIONS_ENTRY_DELAY, 30}, 79.125 + {OPTIONS_PIECE_SET, LJRAND_4BLK}, 79.126 + {OPTIONS_RANDOMIZER, LJRAND_HIST_6}, 79.127 + {OPTIONS_HOLD_PIECE, LJHOLD_NONE}, 79.128 + {OPTIONS_ROTATION_SYSTEM, LJROT_ARIKA}, 79.129 + {OPTIONS_FLOOR_KICKS, 0}, 79.130 + {OPTIONS_LOCKDOWN, LJLOCK_STEP}, 79.131 + {OPTIONS_LOCK_DELAY, 30}, 79.132 + {OPTIONS_BOTTOM_BLOCKS, 0}, 79.133 + {OPTIONS_LINE_DELAY, 0}, 79.134 + {OPTIONS_CLEAR_GRAVITY, LJGRAV_NAIVE}, 79.135 + {OPTIONS_GLUING, LJGLUING_STICKY}, 79.136 + {OPTIONS_GARBAGE, LJGARBAGE_NONE}, 79.137 + {OPTIONS_SIDEWAYS_DELAY, 16}, 79.138 + {OPTIONS_SIDEWAYS_SPEED, 1}, 79.139 + {OPTIONS_INITIAL_SIDEWAYS, 0}, 79.140 + {OPTIONS_IRS, 1}, 79.141 + {OPTIONS_NEXT_PIECES, 1}, 79.142 + END_OF_PRESET 79.143 + }}, 79.144 + 79.145 + {"Square", { 79.146 + {OPTIONS_GIMMICK, LJGM_ATYPE}, 79.147 + {OPTIONS_WIDTH, 10}, 79.148 + {OPTIONS_HEIGHT, 20}, 79.149 + {OPTIONS_ENTER_ABOVE, 1}, 79.150 + {OPTIONS_ENTRY_DELAY, 30}, 79.151 + {OPTIONS_SPEED_CURVE, LJSPD_EXP}, 79.152 + {OPTIONS_PIECE_SET, LJRAND_4BLK}, 79.153 + {OPTIONS_RANDOMIZER, LJRAND_PURE}, 79.154 + {OPTIONS_HOLD_PIECE, LJHOLD_TNT}, 79.155 + {OPTIONS_ROTATION_SYSTEM, LJROT_SRS}, 79.156 + {OPTIONS_FLOOR_KICKS, 6}, 79.157 + {OPTIONS_LOCKDOWN, LJLOCK_STEP}, 79.158 + {OPTIONS_LOCK_DELAY, 30}, 79.159 + {OPTIONS_BOTTOM_BLOCKS, 0}, 79.160 + {OPTIONS_LINE_DELAY, 40}, 79.161 + {OPTIONS_CLEAR_GRAVITY, LJGRAV_NAIVE}, 79.162 + {OPTIONS_GLUING, LJGLUING_SQUARE}, 79.163 + {OPTIONS_SCORING, LJSCORE_TNT64}, 79.164 + {OPTIONS_DROP_SCORING, LJDROP_NOSCORE}, 79.165 + {OPTIONS_T_SPIN, LJTS_TNT}, 79.166 + {OPTIONS_GARBAGE, LJGARBAGE_NONE}, 79.167 + {OPTIONS_SIDEWAYS_DELAY, 11}, 79.168 + {OPTIONS_SIDEWAYS_SPEED, 5}, 79.169 + {OPTIONS_SOFT_DROP_SPEED, 2}, 79.170 + {OPTIONS_SOFT_DROP, LJZANGI_SLIDE}, 79.171 + {OPTIONS_HARD_DROP, LJZANGI_SLIDE}, 79.172 + {OPTIONS_INITIAL_SIDEWAYS, 0}, 79.173 + {OPTIONS_IRS, 0}, 79.174 + {OPTIONS_NEXT_PIECES, 3}, 79.175 + END_OF_PRESET 79.176 + }}, 79.177 + 79.178 + /* http://www.tetrisconcept.com/forum/viewtopic.php?t=549 */ 79.179 + {"40 Lines", { 79.180 + {OPTIONS_GIMMICK, LJGM_BTYPE}, 79.181 + {OPTIONS_WIDTH, 10}, 79.182 + {OPTIONS_PIECE_SET, LJRAND_4BLK}, 79.183 + {OPTIONS_BOTTOM_BLOCKS, 0}, 79.184 + {OPTIONS_CLEAR_GRAVITY, LJGRAV_NAIVE}, 79.185 + {OPTIONS_GARBAGE, 0}, 79.186 + END_OF_PRESET 79.187 + }}, 79.188 + 79.189 + /* http://www.tetrisconcept.com/forum/viewtopic.php?t=549 */ 79.190 + {"180 Seconds", { 79.191 + {OPTIONS_GIMMICK, LJGM_ULTRA}, 79.192 + {OPTIONS_WIDTH, 10}, 79.193 + {OPTIONS_PIECE_SET, LJRAND_4BLK}, 79.194 + {OPTIONS_HOLD_PIECE, LJHOLD_EMPTY}, 79.195 + {OPTIONS_BOTTOM_BLOCKS, 0}, 79.196 + {OPTIONS_CLEAR_GRAVITY, LJGRAV_NAIVE}, 79.197 + {OPTIONS_GLUING, 0}, 79.198 + {OPTIONS_SCORING, LJSCORE_LJ}, 79.199 + {OPTIONS_DROP_SCORING, 0}, 79.200 + {OPTIONS_T_SPIN, 0}, 79.201 + {OPTIONS_GARBAGE, 0}, 79.202 + END_OF_PRESET 79.203 + }}, 79.204 + 79.205 + /* http://www.tetrisconcept.com/forum/viewtopic.php?t=892 */ 79.206 + {"Death 300", { 79.207 + {OPTIONS_GIMMICK, LJGM_ULTRA}, 79.208 + {OPTIONS_SPEED_CURVE, LJSPD_DEATH300}, 79.209 + {OPTIONS_WIDTH, 10}, 79.210 + {OPTIONS_HEIGHT, 20}, 79.211 + {OPTIONS_ENTER_ABOVE, 0}, 79.212 + {OPTIONS_ENTRY_DELAY, 30}, 79.213 + {OPTIONS_PIECE_SET, LJRAND_4BLK}, 79.214 + {OPTIONS_RANDOMIZER, LJRAND_HIST_6}, 79.215 + {OPTIONS_HOLD_PIECE, LJHOLD_EMPTY}, 79.216 + {OPTIONS_ROTATION_SYSTEM, LJROT_ARIKA}, 79.217 + {OPTIONS_FLOOR_KICKS, 0}, 79.218 + {OPTIONS_LOCKDOWN, LJLOCK_STEP}, 79.219 + {OPTIONS_LOCK_DELAY, 30}, 79.220 + {OPTIONS_BOTTOM_BLOCKS, 0}, 79.221 + {OPTIONS_LINE_DELAY, 0}, 79.222 + {OPTIONS_CLEAR_GRAVITY, LJGRAV_NAIVE}, 79.223 + {OPTIONS_GLUING, LJGLUING_STICKY}, 79.224 + {OPTIONS_GARBAGE, LJGARBAGE_NONE}, 79.225 + {OPTIONS_SIDEWAYS_DELAY, 16}, 79.226 + {OPTIONS_SIDEWAYS_SPEED, 1}, 79.227 + {OPTIONS_INITIAL_SIDEWAYS, 0}, 79.228 + {OPTIONS_IRS, 1}, 79.229 + {OPTIONS_NEXT_PIECES, 1}, 79.230 + END_OF_PRESET 79.231 + }}, 79.232 + 79.233 + {"M-Roll", { 79.234 + {OPTIONS_GIMMICK, LJGM_BTYPE}, 79.235 + {OPTIONS_SPEED_CURVE, LJSPD_DEATH300}, 79.236 + {OPTIONS_WIDTH, 10}, 79.237 + {OPTIONS_HEIGHT, 20}, 79.238 + {OPTIONS_ENTER_ABOVE, 0}, 79.239 + {OPTIONS_ENTRY_DELAY, 30}, 79.240 + {OPTIONS_PIECE_SET, LJRAND_4BLK}, 79.241 + {OPTIONS_RANDOMIZER, LJRAND_HIST_6}, 79.242 + {OPTIONS_HOLD_PIECE, LJHOLD_EMPTY}, 79.243 + {OPTIONS_ROTATION_SYSTEM, LJROT_ARIKA}, 79.244 + {OPTIONS_FLOOR_KICKS, 0}, 79.245 + {OPTIONS_LOCKDOWN, LJLOCK_STEP}, 79.246 + {OPTIONS_LOCK_DELAY, 30}, 79.247 + {OPTIONS_BOTTOM_BLOCKS, 0}, 79.248 + {OPTIONS_LINE_DELAY, 0}, 79.249 + {OPTIONS_CLEAR_GRAVITY, LJGRAV_NAIVE}, 79.250 + {OPTIONS_GLUING, LJGLUING_STICKY}, 79.251 + {OPTIONS_GARBAGE, LJGARBAGE_NONE}, 79.252 + {OPTIONS_SIDEWAYS_DELAY, 16}, 79.253 + {OPTIONS_SIDEWAYS_SPEED, 1}, 79.254 + {OPTIONS_INITIAL_SIDEWAYS, 0}, 79.255 + {OPTIONS_IRS, 1}, 79.256 + {OPTIONS_NEXT_PIECES, 1}, 79.257 + {OPTIONS_HIDE_PF, 1}, 79.258 + END_OF_PRESET 79.259 + }}, 79.260 + 79.261 + /* http://www.tetrisconcept.com/forum/viewtopic.php?t=549 */ 79.262 + {"Cascade", { 79.263 + {OPTIONS_GIMMICK, LJGM_BTYPE}, 79.264 + {OPTIONS_WIDTH, 10}, 79.265 + {OPTIONS_PIECE_SET, LJRAND_4BLK}, 79.266 + {OPTIONS_HOLD_PIECE, LJHOLD_EMPTY}, 79.267 + {OPTIONS_CLEAR_GRAVITY, LJGRAV_CASCADE}, 79.268 + {OPTIONS_GARBAGE, 0}, 79.269 + END_OF_PRESET 79.270 + }}, 79.271 + 79.272 + /* http://www.tetrisconcept.com/forum/viewtopic.php?t=777 */ 79.273 + {"Baboo!", { 79.274 + {OPTIONS_GIMMICK, LJGM_BABY}, 79.275 + {OPTIONS_WIDTH, 10}, 79.276 + {OPTIONS_PIECE_SET, LJRAND_4BLK}, 79.277 + {OPTIONS_SPEED_CURVE, LJSPD_ZERO}, 79.278 + {OPTIONS_BOTTOM_BLOCKS, 0}, 79.279 + {OPTIONS_CLEAR_GRAVITY, LJGRAV_NAIVE}, 79.280 + {OPTIONS_GLUING, 0}, 79.281 + {OPTIONS_SCORING, LJSCORE_LJ}, 79.282 + {OPTIONS_DROP_SCORING, 0}, 79.283 + {OPTIONS_T_SPIN, LJTS_TDS}, 79.284 + {OPTIONS_GARBAGE, 0} 79.285 + }}, 79.286 + 79.287 +}; 79.288 + 79.289 +const Preset *loadedPresets = predefPresets; 79.290 +size_t nLoadedPresets = sizeof(predefPresets)/sizeof(predefPresets[0]); 79.291 + 79.292 +static unsigned char presetBuffer[OPTIONS_MENU_LEN]; 79.293 + 79.294 +void presetStart(void) { 79.295 + memset(&presetBuffer, 255, sizeof(presetBuffer)); 79.296 +} 79.297 + 79.298 +void presetAdd(size_t which) { 79.299 + if (which >= nLoadedPresets) { 79.300 + return; 79.301 + } 79.302 + const PresetRule *r = loadedPresets[which].rules; 79.303 + for (size_t i = 0; 79.304 + i < PRESET_MAX_RULES 79.305 + && r[i].line < OPTIONS_MENU_LEN; 79.306 + ++i) { 79.307 + presetBuffer[r[i].line] = r[i].value; 79.308 + } 79.309 +} 79.310 + 79.311 +void presetFinish(struct LJView *v) { 79.312 + unpackCommonOptions(v, presetBuffer); 79.313 +} 79.314 + 79.315 +#if 0 79.316 +void loadPresetsFromText(FILE *fp) { 79.317 + Preset *presets = NULL; 79.318 + 79.319 + size_t capacity = 0, n = 0; 79.320 + 79.321 + { 79.322 + // allocate memory for this preset 79.323 + if (capacity <= n) { 79.324 + if (capacity < 32) { 79.325 + capacity += 16; 79.326 + } else { 79.327 + capacity += capacity / 2; 79.328 + } 79.329 + Preset *newPresets = realloc(presets, 79.330 + capacity * sizeof(preset)); 79.331 + // if we can't allocate memory, screw it 79.332 + if (!newPresets) { 79.333 + break; 79.334 + } 79.335 + } 79.336 + } 79.337 + 79.338 + loadedPresets = presets; 79.339 + nLoadedPresets = n; 79.340 +} 79.341 +#endif 79.342 +
80.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 80.2 +++ b/src/scenario.h Fri Mar 13 00:39:12 2009 -0700 80.3 @@ -0,0 +1,60 @@ 80.4 +/* PC preset code for LOCKJAW, an implementation of the Soviet Mind Game 80.5 + 80.6 +Copyright (C) 2008 Damian Yerrick <tepples+lj@spamcop.net> 80.7 + 80.8 +This work is free software; you can redistribute it and/or modify 80.9 +it under the terms of the GNU General Public License as published by 80.10 +the Free Software Foundation; either version 2 of the License, or 80.11 +(at your option) any later version. 80.12 + 80.13 +This program is distributed in the hope that it will be useful, 80.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 80.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 80.16 +GNU General Public License for more details. 80.17 + 80.18 +You should have received a copy of the GNU General Public License 80.19 +along with this program; if not, write to the Free Software 80.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 80.21 + 80.22 +Original game concept and design by Alexey Pajitnov. 80.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 80.24 +or The Tetris Company LLC. 80.25 + 80.26 +*/ 80.27 + 80.28 +#ifndef PCPRESET_H 80.29 +#define PCPRESET_H 80.30 +#include <sys/types.h> 80.31 +#include "ljcontrol.h" 80.32 + 80.33 +typedef struct PresetRule { 80.34 + unsigned char line, value; 80.35 +} PresetRule; 80.36 + 80.37 +#define PRESET_NAME_LEN 32 80.38 +#define PRESET_MAX_RULES 32 80.39 + 80.40 +typedef struct Preset { 80.41 + char name[PRESET_NAME_LEN]; 80.42 + PresetRule rules[PRESET_MAX_RULES]; 80.43 +} Preset; 80.44 + 80.45 +/** 80.46 + * Resets all options in the preset buffer to inherit. 80.47 + */ 80.48 +void presetStart(void); 80.49 + 80.50 +/** 80.51 + * Adds a preset to the preset buffer. 80.52 + */ 80.53 +void presetAdd(size_t which); 80.54 + 80.55 +/** 80.56 + * Unpacks the preset buffer onto a view. 80.57 + */ 80.58 +void presetFinish(struct LJView *v); 80.59 + 80.60 +extern const Preset *loadedPresets; 80.61 +extern size_t nLoadedPresets; 80.62 + 80.63 +#endif
81.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 81.2 +++ b/src/speed.c Fri Mar 13 00:39:12 2009 -0700 81.3 @@ -0,0 +1,431 @@ 81.4 +/* Speed curve tables for LOCKJAW, an implementation of the Soviet Mind Game 81.5 + 81.6 +Copyright (C) 2006-2007 Damian Yerrick <tepples+lj@spamcop.net> 81.7 + 81.8 +This work is free software; you can redistribute it and/or modify 81.9 +it under the terms of the GNU General Public License as published by 81.10 +the Free Software Foundation; either version 2 of the License, or 81.11 +(at your option) any later version. 81.12 + 81.13 +This program is distributed in the hope that it will be useful, 81.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 81.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 81.16 +GNU General Public License for more details. 81.17 + 81.18 +You should have received a copy of the GNU General Public License 81.19 +along with this program; if not, write to the Free Software 81.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 81.21 + 81.22 +Original game concept and design by Alexey Pajitnov. 81.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 81.24 +or The Tetris Company LLC. 81.25 + 81.26 +*/ 81.27 +#include "lj.h" 81.28 +#include "ljcontrol.h" 81.29 + 81.30 +static const LJFixed initialGravity[LJSPD_N_CURVES] = { 81.31 + [LJSPD_RHYTHMZERO] = 0, 81.32 + [LJSPD_RHYTHM] = LJITOFIX(20) 81.33 +}; 81.34 + 81.35 +/** 81.36 + * The default speed curve for each gimmick. A negative value 81.37 + * means that the gimmick uses the player's chosen speed curve. 81.38 + */ 81.39 +static const signed char defaultSpeedCurve[LJGM_N_GIMMICKS] = { 81.40 + [LJGM_ATYPE] = -1, 81.41 + [LJGM_BTYPE] = -1, 81.42 + [LJGM_ULTRA] = -1, 81.43 + [LJGM_DRILL] = -1, 81.44 + [LJGM_ITEMS] = -1, 81.45 + [LJGM_BABY] = LJSPD_ZERO 81.46 +}; 81.47 + 81.48 +/* The new speed curve code ****************************************/ 81.49 + 81.50 +#define SCGRAV(n, d) ((n) * 2048 / (d)) 81.51 + 81.52 +enum { 81.53 + SPEEDTYPE_RHYTHM, 81.54 + SPEEDTYPE_TGM, 81.55 + SPEEDTYPE_LINES, 81.56 + SPEEDTYPE_PIECES 81.57 +}; 81.58 + 81.59 +typedef struct LJSpeedStep { 81.60 + unsigned short level, gravity; 81.61 + signed char are, das, lock, line; 81.62 +} LJSpeedStep; 81.63 + 81.64 +typedef struct LJSpeedCurve { 81.65 + char name[32]; 81.66 + unsigned char type, sectionLen; 81.67 + unsigned char nSteps; 81.68 + const LJSpeedStep steps[]; 81.69 +} LJSpeedCurve; 81.70 + 81.71 +const LJSpeedCurve scMaster = { 81.72 + "Master", 81.73 + SPEEDTYPE_TGM, 100, 81.74 + 33, 81.75 + { 81.76 + { 0, SCGRAV( 4,256),25,16,30,40}, 81.77 + { 30, SCGRAV( 6,256),25,16,30,40}, 81.78 + { 35, SCGRAV( 8,256),25,16,30,40}, 81.79 + { 40, SCGRAV( 10,256),25,16,30,40}, 81.80 + { 50, SCGRAV( 12,256),25,16,30,40}, 81.81 + { 60, SCGRAV( 16,256),25,16,30,40}, 81.82 + { 70, SCGRAV( 32,256),25,16,30,40}, 81.83 + { 80, SCGRAV( 48,256),25,16,30,40}, 81.84 + { 90, SCGRAV( 64,256),25,16,30,40}, 81.85 + {100, SCGRAV( 80,256),25,16,30,40}, 81.86 + {120, SCGRAV( 96,256),25,16,30,40}, 81.87 + {140, SCGRAV(112,256),25,16,30,40}, 81.88 + {160, SCGRAV(128,256),25,16,30,40}, 81.89 + {170, SCGRAV(144,256),25,16,30,40}, 81.90 + {200, SCGRAV( 4,256),25,16,30,40}, 81.91 + {220, SCGRAV( 32,256),25,16,30,40}, 81.92 + {230, SCGRAV( 64,256),25,16,30,40}, 81.93 + {233, SCGRAV( 96,256),25,16,30,40}, 81.94 + {236, SCGRAV(128,256),25,16,30,40}, 81.95 + {239, SCGRAV(160,256),25,16,30,40}, 81.96 + {243, SCGRAV(192,256),25,16,30,40}, 81.97 + {247, SCGRAV(224,256),25,16,30,40}, 81.98 + {251, SCGRAV( 1,1), 25,16,30,40}, 81.99 + {300, SCGRAV( 2,1), 25,16,30,40}, 81.100 + {330, SCGRAV( 3,1), 25,16,30,40}, 81.101 + {360, SCGRAV( 4,1), 25,16,30,40}, 81.102 + {400, SCGRAV( 5,1), 25,16,30,40}, 81.103 + {450, SCGRAV( 3,1), 25,16,30,40}, 81.104 + {500, SCGRAV(20,1), 25,10,30,25}, 81.105 + {600, SCGRAV(20,1), 25,10,30,16}, 81.106 + {700, SCGRAV(20,1), 16,10,30,12}, 81.107 + {800, SCGRAV(20,1), 12,10,30, 6}, 81.108 + {900, SCGRAV(20,1), 12, 8,17, 6} 81.109 + } 81.110 +}; 81.111 + 81.112 +const LJSpeedCurve scDeath = { 81.113 + "Death", 81.114 + SPEEDTYPE_TGM, 100, 81.115 + 6, 81.116 + { 81.117 + { 0, SCGRAV(20,1), 18,12,30, 8}, 81.118 + {100, SCGRAV(20,1), 14,12,26, 0}, 81.119 + {200, SCGRAV(20,1), 14,11,22, 0}, 81.120 + {300, SCGRAV(20,1), 8,10,18, 6}, 81.121 + {400, SCGRAV(20,1), 7, 8,15, 5}, 81.122 + {500, SCGRAV(20,1), 6, 8,15, 4} 81.123 + } 81.124 +}; 81.125 + 81.126 +const LJSpeedCurve scNES = { 81.127 + "NES", 81.128 + SPEEDTYPE_LINES, 10, 81.129 + 15, 81.130 + { 81.131 + { 0, SCGRAV(1,48), 10,-1, 0,30}, 81.132 + { 10, SCGRAV(1,43), 10,-1, 0,30}, 81.133 + { 20, SCGRAV(1,38), 10,-1, 0,30}, 81.134 + { 30, SCGRAV(1,33), 10,-1, 0,30}, 81.135 + { 40, SCGRAV(1,28), 10,-1, 0,30}, 81.136 + { 50, SCGRAV(1,23), 10,-1, 0,30}, 81.137 + { 60, SCGRAV(1,18), 10,-1, 0,30}, 81.138 + { 70, SCGRAV(1,13), 10,-1, 0,30}, 81.139 + { 80, SCGRAV(1, 8), 10,-1, 0,30}, 81.140 + { 90, SCGRAV(1, 6), 10,-1, 0,30}, 81.141 + {100, SCGRAV(1, 5), 10,-1, 0,30}, 81.142 + {130, SCGRAV(1, 4), 10,-1, 0,30}, 81.143 + {160, SCGRAV(1, 3), 10,-1, 0,30}, 81.144 + {190, SCGRAV(1, 2), 10,-1, 0,30}, 81.145 + {290, SCGRAV(1, 1), 10,-1, 0,30}, 81.146 + } 81.147 +}; 81.148 + 81.149 +const LJSpeedCurve scGB = { 81.150 + "Game Boy", 81.151 + SPEEDTYPE_LINES, 10, 81.152 + 18, 81.153 + { 81.154 + { 0, SCGRAV(1,53), 0,-1, 0,90}, 81.155 + { 10, SCGRAV(1,49), 0,-1, 0,90}, 81.156 + { 20, SCGRAV(1,45), 0,-1, 0,90}, 81.157 + { 30, SCGRAV(1,41), 0,-1, 0,90}, 81.158 + { 40, SCGRAV(1,37), 0,-1, 0,90}, 81.159 + { 50, SCGRAV(1,33), 0,-1, 0,90}, 81.160 + { 60, SCGRAV(1,28), 0,-1, 0,90}, 81.161 + { 70, SCGRAV(1,22), 0,-1, 0,90}, 81.162 + { 80, SCGRAV(1,17), 0,-1, 0,90}, 81.163 + { 90, SCGRAV(1,11), 0,-1, 0,90}, 81.164 + {100, SCGRAV(1,10), 0,-1, 0,90}, 81.165 + {110, SCGRAV(1, 9), 0,-1, 0,90}, 81.166 + {120, SCGRAV(1, 8), 0,-1, 0,90}, 81.167 + {130, SCGRAV(1, 7), 0,-1, 0,90}, 81.168 + {140, SCGRAV(1, 6), 0,-1, 0,90}, 81.169 + {160, SCGRAV(1, 5), 0,-1, 0,90}, 81.170 + {180, SCGRAV(1, 4), 0,-1, 0,90}, 81.171 + {200, SCGRAV(1, 3), 0,-1, 0,90}, 81.172 + } 81.173 +}; 81.174 + 81.175 +const LJSpeedCurve scZero = { 81.176 + "Zero", 81.177 + SPEEDTYPE_LINES, 10, 81.178 + 1, 81.179 + { 81.180 + { 0, SCGRAV(0, 1),-1,-1,40,-1} 81.181 + } 81.182 +}; 81.183 + 81.184 +const LJSpeedCurve scExponential = { 81.185 + "Exponential", 81.186 + SPEEDTYPE_PIECES, 60, 81.187 + 34, 81.188 + { 81.189 + { 0, SCGRAV( 1,60),-1,-1,40,-1 }, 81.190 + { 30, SCGRAV( 1,42),-1,-1,40,-1 }, 81.191 + { 60, SCGRAV( 2,60),-1,-1,40,-1 }, 81.192 + { 90, SCGRAV( 2,42),-1,-1,40,-1 }, 81.193 + {120, SCGRAV( 4,60),-1,-1,40,-1 }, 81.194 + {150, SCGRAV( 4,42),-1,-1,40,-1 }, 81.195 + {180, SCGRAV( 8,60),-1,-1,40,-1 }, 81.196 + {210, SCGRAV( 8,42),-1,-1,40,-1 }, 81.197 + {240, SCGRAV( 16,60),-1,-1,40,-1 }, 81.198 + {270, SCGRAV( 16,42),-1,-1,40,-1 }, 81.199 + {300, SCGRAV( 32,60),-1,-1,40,-1 }, 81.200 + {330, SCGRAV( 32,42),-1,-1,40,-1 }, 81.201 + {360, SCGRAV( 64,60),-1,-1,40,-1 }, 81.202 + {390, SCGRAV( 64,42),-1,-1,40,-1 }, 81.203 + {420, SCGRAV(128,60),-1,-1,40,-1 }, 81.204 + {450, SCGRAV(128,42),-1,-1,40,-1 }, 81.205 + {480, SCGRAV(256,60),-1,-1,40,-1 }, 81.206 + {510, SCGRAV(256,42),-1,-1,40,-1 }, 81.207 + {540, SCGRAV(512,60),-1,-1,40,-1 }, 81.208 + {570, SCGRAV(512,42),-1,-1,40,-1 }, 81.209 + {600, SCGRAV( 20, 1),-1,-1,40,-1 }, 81.210 + {630, SCGRAV( 20, 1),-1,-1,30,-1 }, 81.211 + {660, SCGRAV( 20, 1),-1,-1,24,-1 }, 81.212 + {690, SCGRAV( 20, 1),-1,-1,20,-1 }, 81.213 + {720, SCGRAV( 20, 1),-1,-1,17,-1 }, 81.214 + {750, SCGRAV( 20, 1),-1,-1,15,-1 }, 81.215 + {780, SCGRAV( 20, 1),-1,-1,13,-1 }, 81.216 + {810, SCGRAV( 20, 1),-1,-1,12,-1 }, 81.217 + {840, SCGRAV( 20, 1),-1,-1,11,-1 }, 81.218 + {870, SCGRAV( 20, 1),-1,-1,10,-1 }, 81.219 + {900, SCGRAV( 20, 1),-1,-1, 9,-1 }, 81.220 + {930, SCGRAV( 20, 1),-1,-1, 8,-1 }, 81.221 + {960, SCGRAV( 20, 1),-1,-1, 7,-1 }, 81.222 + {990, SCGRAV( 20, 1),-1,-1, 6,-1 } 81.223 + } 81.224 +}; 81.225 + 81.226 +static const LJSpeedCurve *const newSpeedCurves[LJSPD_N_CURVES] = { 81.227 + [LJSPD_EXP] = &scExponential, 81.228 + [LJSPD_ZERO] = &scZero, 81.229 + [LJSPD_TGM] = &scMaster, 81.230 + [LJSPD_DEATH] = &scDeath, 81.231 + [LJSPD_DEATH300] = &scDeath, 81.232 + [LJSPD_NES] = &scNES, 81.233 + [LJSPD_GB] = &scGB, 81.234 + [LJSPD_GBHEART] = &scGB, 81.235 +}; 81.236 + 81.237 +/** 81.238 + * Performs a fast binary search of a speed step table. 81.239 + */ 81.240 +static const LJSpeedStep *getSpeedStep(const LJSpeedStep *steps, 81.241 + size_t nSteps, int level) { 81.242 + unsigned int lo = 0; 81.243 + unsigned int hi = nSteps; 81.244 + 81.245 + while (hi - lo > 1) { 81.246 + size_t mid = (hi + lo) / 2; 81.247 + unsigned int here = steps[mid].level; 81.248 + if (here == level) { 81.249 + return &(steps[mid]); 81.250 + } else if (here < level) { 81.251 + lo = mid; 81.252 + } else { 81.253 + hi = mid; 81.254 + } 81.255 + } 81.256 + return &(steps[lo]); 81.257 +} 81.258 + 81.259 +/** 81.260 + * Updates the level after each piece has retired. 81.261 + * @return nonzero iff a new section has happened 81.262 + */ 81.263 +int updLevelAfterPiece(LJField *p) { 81.264 + int curveID = p->speedState.curve; 81.265 + const LJSpeedCurve *curve = newSpeedCurves[curveID]; 81.266 + if (!curve) { 81.267 + return 0; 81.268 + } 81.269 + unsigned int sectionLen = curve->sectionLen; 81.270 + unsigned int oldLevel = p->speedState.level; 81.271 + unsigned int oldSection = oldLevel / sectionLen; 81.272 + unsigned int oldSectionPos = oldLevel % sectionLen; 81.273 + 81.274 + switch (curve->type) { 81.275 + case SPEEDTYPE_TGM: 81.276 + if (oldSectionPos + 1 >= sectionLen) { 81.277 + return 0; 81.278 + } 81.279 + // otherwise fall through to +1 per piece 81.280 + 81.281 + case SPEEDTYPE_PIECES: 81.282 + p->speedState.level = oldLevel + 1; 81.283 + break; 81.284 + 81.285 + default: 81.286 + return 0; 81.287 + } 81.288 + 81.289 + unsigned int newSection = p->speedState.level / sectionLen; 81.290 + return newSection > oldSection; 81.291 +} 81.292 + 81.293 +/** 81.294 + * Updates the level after lines have been cleared. 81.295 + * @return nonzero iff a new section has happened 81.296 + */ 81.297 +int updLevelAfterLines(LJField *p, unsigned int nLines) { 81.298 + int curveID = p->speedState.curve; 81.299 + const LJSpeedCurve *curve = newSpeedCurves[curveID]; 81.300 + if (!curve) { 81.301 + return 0; 81.302 + } 81.303 + unsigned int sectionLen = curve->sectionLen; 81.304 + unsigned int oldLevel = p->speedState.level; 81.305 + unsigned int oldSection = oldLevel / sectionLen; 81.306 + 81.307 + switch (curve->type) { 81.308 + case SPEEDTYPE_TGM: 81.309 + case SPEEDTYPE_LINES: 81.310 + p->speedState.level = oldLevel + nLines; 81.311 + break; 81.312 + 81.313 + default: 81.314 + return 0; 81.315 + } 81.316 + 81.317 + unsigned int newSection = p->speedState.level / sectionLen; 81.318 + return newSection > oldSection; 81.319 +} 81.320 + 81.321 +void setSpeedNew(LJField *p, LJControl *c) { 81.322 + int curveID = p->speedState.curve; 81.323 + const LJSpeedCurve *curve = newSpeedCurves[curveID]; 81.324 + if (!curve) { 81.325 + return; 81.326 + } 81.327 + const LJSpeedStep *step = 81.328 + getSpeedStep(curve->steps, curve->nSteps, p->speedState.level); 81.329 + 81.330 + p->speed.gravity = step->gravity << 5; 81.331 + 81.332 + p->speed.entryDelay = step->are; 81.333 + if (p->speed.entryDelay > p->areStyle) { 81.334 + p->speed.entryDelay = p->areStyle; 81.335 + } 81.336 + 81.337 + if (step->das > 0 && c->dasDelay > step->das) { 81.338 + c->dasDelay = step->das; 81.339 + } 81.340 + 81.341 + if (p->setLockDelay >= 128) { 81.342 + p->speed.lockDelay = 127; 81.343 + } else if (p->setLockDelay > 0) { 81.344 + p->speed.lockDelay = p->setLockDelay; 81.345 + } else if (step->lock > 0) { 81.346 + p->speed.lockDelay = step->lock; 81.347 + } 81.348 + 81.349 + if (p->setLineDelay > 0) { 81.350 + p->speed.lineDelay = p->setLineDelay; 81.351 + } else if (step->line >= 0) { 81.352 + p->speed.lineDelay = step->line; 81.353 + } else { 81.354 + p->speed.lineDelay = p->speed.entryDelay; 81.355 + } 81.356 +} 81.357 + 81.358 + 81.359 + 81.360 +/* Old speed curve system is below this line ***********************/ 81.361 + 81.362 + 81.363 + 81.364 +void initSpeed(LJField *p) { 81.365 + if (defaultSpeedCurve[p->gimmick] >= 0) { 81.366 + p->speedState.curve = defaultSpeedCurve[p->gimmick]; 81.367 + } 81.368 + p->speed.gravity = initialGravity[p->speedState.curve]; 81.369 + 81.370 + switch (p->speedState.curve) { 81.371 + case LJSPD_RHYTHM: 81.372 + case LJSPD_RHYTHMZERO: 81.373 + p->speedState.level = 60; 81.374 + p->bpmCounter = 0; 81.375 + p->speedupCounter = 0; 81.376 + break; 81.377 + case LJSPD_TGM: 81.378 + case LJSPD_DEATH: 81.379 + case LJSPD_EXP: 81.380 + p->speedState.level = -1; 81.381 + break; 81.382 + case LJSPD_DEATH300: 81.383 + p->speedState.level = 300; 81.384 + break; 81.385 + case LJSPD_GBHEART: 81.386 + p->speedState.level = 100; 81.387 + break; 81.388 + default: 81.389 + p->speedState.level = 0; 81.390 + break; 81.391 + } 81.392 +} 81.393 + 81.394 + 81.395 +void setSpeed(LJField *p, LJControl *c) { 81.396 + p->speed.entryDelay = p->areStyle; 81.397 + 81.398 + // Default line delay is equal to the entry delay, 81.399 + // but speed curves and setLineDelay can override this 81.400 + p->speed.lineDelay = p->speed.entryDelay; 81.401 + switch (p->speedState.curve) { 81.402 + 81.403 + case LJSPD_RHYTHM: 81.404 + case LJSPD_RHYTHMZERO: 81.405 + // If we've already banked five pieces' worth of time, 81.406 + // add 20 points instead of banking another. 81.407 + if (p->bpmCounter <= -18000) { 81.408 + // removed in 0.21 because other curves don't reward for drops 81.409 + // p->score += 20; 81.410 + } else { 81.411 + p->bpmCounter -= 3600; // number of frames per minute 81.412 + } 81.413 + p->speed.lockDelay = 3600 / p->speedState.level; 81.414 + p->speed.gravity = (p->speedState.curve == LJSPD_RHYTHM) ? ljitofix(20) : 0; 81.415 + break; 81.416 + 81.417 + default: 81.418 + if (updLevelAfterPiece(p)) { 81.419 + p->sounds |= LJSND_SECTIONUP; 81.420 + } 81.421 + setSpeedNew(p, c); 81.422 + break; 81.423 + } 81.424 + 81.425 + if (p->setLockDelay >= 128) { 81.426 + p->speed.lockDelay = 127; 81.427 + } else if (p->setLockDelay > 0) { 81.428 + p->speed.lockDelay = p->setLockDelay; 81.429 + } 81.430 + if (p->setLineDelay > 0) { 81.431 + p->speed.lineDelay = p->setLineDelay; 81.432 + } 81.433 +} 81.434 +
82.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 82.2 +++ b/src/talkback.h Fri Mar 13 00:39:12 2009 -0700 82.3 @@ -0,0 +1,24 @@ 82.4 +/* 82.5 +This struct lives in the ARM9's memory. It is used to send data back 82.6 +to the ARM7. 82.7 + 82.8 +By Damian Yerrick. No rights reserved and ABSOLUTELY NO WARRANTY. 82.9 +*/ 82.10 +#ifndef TALKBACK_H 82.11 +#define TALKBACK_H 82.12 + 82.13 +typedef struct P8A7Talkback { 82.14 + unsigned long int sounds; 82.15 + signed char countdown; 82.16 + unsigned char cmd; 82.17 +} P8A7Talkback; 82.18 + 82.19 +enum { 82.20 + TALKBACK_NONE = 0, 82.21 + TALKBACK_POWER_OFF = 1, 82.22 + TALKBACK_PLAY_MUSIC = 2, 82.23 + TALKBACK_STOP_MUSIC = 3, 82.24 + TALKBACK_PAUSE_MUSIC = 4 82.25 +}; 82.26 + 82.27 +#endif
83.1 Binary file src/text.chr has changed
84.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 84.2 +++ b/src/winicon.rc Fri Mar 13 00:39:12 2009 -0700 84.3 @@ -0,0 +1,1 @@ 84.4 +allegro_icon ICON PRELOAD "docs/appicon.ico"
85.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 85.2 +++ b/src/wktables.c Fri Mar 13 00:39:12 2009 -0700 85.3 @@ -0,0 +1,454 @@ 85.4 +/* Wall kick tables for LOCKJAW, an implementation of the Soviet Mind Game 85.5 + 85.6 +Copyright (C) 2006 Damian Yerrick <tepples+lj@spamcop.net> 85.7 + 85.8 +This work is free software; you can redistribute it and/or modify 85.9 +it under the terms of the GNU General Public License as published by 85.10 +the Free Software Foundation; either version 2 of the License, or 85.11 +(at your option) any later version. 85.12 + 85.13 +This program is distributed in the hope that it will be useful, 85.14 +but WITHOUT ANY WARRANTY; without even the implied warranty of 85.15 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 85.16 +GNU General Public License for more details. 85.17 + 85.18 +You should have received a copy of the GNU General Public License 85.19 +along with this program; if not, write to the Free Software 85.20 +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 85.21 + 85.22 +Original game concept and design by Alexey Pajitnov. 85.23 +The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg, 85.24 +or The Tetris Company LLC. 85.25 + 85.26 +*/ 85.27 + 85.28 +#define LJ_INTERNAL 85.29 +#include "lj.h" 85.30 + 85.31 + 85.32 +// These wall kicks are for rotation TO a given orientation. 85.33 +// Based on http://www.the-shell.net/img/srs_study.html 85.34 +static const LJRotSystem rotSRS = { 85.35 + .kicksL = {1, 0, 0, -1, 0, 0, 0, -1, 0, -1}, 85.36 + .kicksR = {3, 2, 2, -1, 2, 2, 2, -1, 2, -3}, 85.37 + .kickTables = { 85.38 + 85.39 + // 0: JLSTZ counterclockwise 85.40 + { 85.41 + { WK( 0, 0),WK( 1, 0),WK( 1,-1),WK( 0, 2),WK( 1, 2) }, // R->U 85.42 + { WK( 0, 0),WK(-1, 0),WK(-1, 1),WK( 0,-2),WK(-1,-2) }, // D->R 85.43 + { WK( 0, 0),WK(-1, 0),WK(-1,-1),WK( 0, 2),WK(-1, 2) }, // L->D 85.44 + { WK( 0, 0),WK( 1, 0),WK( 1, 1),WK( 0,-2),WK( 1,-2) } // U->L 85.45 + }, 85.46 + 85.47 + // 1: I counterclockwise 85.48 + { 85.49 + { WK( 0, 0),WK(-1, 0),WK( 2, 0),WK(-1,-2),WK( 2, 1) }, // R->U 85.50 + { WK( 0, 0),WK( 1, 0),WK(-2, 0),WK( 1,-1),WK(-2, 1) }, // D->R 85.51 + { WK( 0, 0),WK( 1, 0),WK(-2, 0),WK(-2,-1),WK( 1, 2) }, // L->D 85.52 + { WK( 0, 0),WK(-1, 0),WK( 2, 0),WK( 2,-1),WK(-1, 2) } // U->L 85.53 + }, 85.54 + 85.55 + // 2: JLSTZ clockwise 85.56 + { 85.57 + { WK( 0, 0),WK(-1, 0),WK(-1,-1),WK( 0, 2),WK(-1, 2) }, // L->U 85.58 + { WK( 0, 0),WK(-1, 0),WK(-1, 1),WK( 0,-2),WK(-1,-2) }, // U->R 85.59 + { WK( 0, 0),WK( 1, 0),WK( 1,-1),WK( 0, 2),WK( 1, 2) }, // R->D 85.60 + { WK( 0, 0),WK( 1, 0),WK( 1, 1),WK( 0,-2),WK( 1,-2) }, // D->L 85.61 + }, 85.62 + 85.63 + // 3: I clockwise 85.64 + { 85.65 + { WK( 0, 0),WK( 1, 0),WK(-2, 0),WK( 1,-2),WK(-2, 1) }, // L->U 85.66 + { WK( 0, 0),WK( 1, 0),WK(-2, 0),WK(-2,-1),WK( 1, 2) }, // U->R 85.67 + { WK( 0, 0),WK(-1, 0),WK( 2, 0),WK( 2,-1),WK(-1, 2) }, // R->D 85.68 + { WK( 0, 0),WK(-1, 0),WK( 2, 0),WK(-1,-1),WK( 2, 1) } // D->L 85.69 + } 85.70 + } 85.71 +}; 85.72 + 85.73 +// I: round to top right 85.74 +// J, L, T: round to bottom 85.75 +// S: round to bottom left (constant center column) 85.76 +// Z: round to bottom right (constant center column) 85.77 +static const LJRotSystem rotSega = { 85.78 + .colorScheme = 1, 85.79 + .entryOffset = { 85.80 + WK( 0, 0),WK( 0, 1),WK( 0, 1),WK( 0, 0),WK( 0, 0),WK( 0, 1),WK( 0, 0), 85.81 + WK( 0, 0),WK( 0, 0),WK( 0, 0) 85.82 + }, 85.83 + .entryTheta = { 0, 2, 2, 0, 0, 2, 0, 0, 0, 2 }, 85.84 + .kicksL = {0, 1, 1, -1, 2, 1, 3, 0, -1, -1}, 85.85 + .kicksR = {4, 5, 5, -1, 6, 5, 7, 4, -1, -1}, 85.86 + 85.87 + .kickTables = { 85.88 + 85.89 + // 0: I counterclockwise 85.90 + { 85.91 + { WK( 0, 0),WK_END }, // R->U 85.92 + { WK( 0,-1),WK_END }, // D->R 85.93 + { WK(-1, 1),WK_END }, // L->D 85.94 + { WK( 1, 0),WK_END } // U->L 85.95 + }, 85.96 + 85.97 + // 1: JLT counterclockwise 85.98 + { 85.99 + { WK( 0,-1),WK_END }, // R->U 85.100 + { WK( 0, 0),WK_END }, // D->R 85.101 + { WK( 0, 0),WK_END }, // L->D 85.102 + { WK( 0, 1),WK_END } // U->L 85.103 + }, 85.104 + 85.105 + // 2: S counterclockwise (round left, like Game Boy) 85.106 + { 85.107 + { WK( 1,-1),WK_END }, // R->U 85.108 + { WK(-1, 0),WK_END }, // D->R 85.109 + { WK( 0, 0),WK_END }, // L->D 85.110 + { WK( 0, 1),WK_END } // U->L 85.111 + }, 85.112 + 85.113 + // 3: Z counterclockwise (round right, like NES) 85.114 + { 85.115 + { WK( 0,-1),WK_END }, // R->U 85.116 + { WK( 0, 0),WK_END }, // D->R 85.117 + { WK(-1, 0),WK_END }, // L->D 85.118 + { WK( 1, 1),WK_END } // U->L 85.119 + }, 85.120 + 85.121 + // 4: I clockwise 85.122 + { 85.123 + { WK(-1, 0),WK_END }, // L->U 85.124 + { WK( 0, 0),WK_END }, // U->R 85.125 + { WK( 0, 1),WK_END }, // R->D 85.126 + { WK( 1,-1),WK_END } // D->L 85.127 + }, 85.128 + 85.129 + // 5: JLT clockwise 85.130 + { 85.131 + { WK( 0,-1),WK_END }, // L->U 85.132 + { WK( 0, 1),WK_END }, // U->R 85.133 + { WK( 0, 0),WK_END }, // R->D 85.134 + { WK( 0, 0),WK_END } // D->L 85.135 + }, 85.136 + 85.137 + // 6: S clockwise (round left) 85.138 + { 85.139 + { WK( 0,-1),WK_END }, // L->U 85.140 + { WK(-1, 1),WK_END }, // U->R 85.141 + { WK( 1, 0),WK_END }, // R->D 85.142 + { WK( 0, 0),WK_END } // D->L 85.143 + }, 85.144 + 85.145 + // 7: Z clockwise (round right) 85.146 + { 85.147 + { WK(-1,-1),WK_END }, // L->U 85.148 + { WK( 0, 1),WK_END }, // U->R 85.149 + { WK( 0, 0),WK_END }, // R->D 85.150 + { WK( 1, 0),WK_END } // D->L 85.151 + } 85.152 + } 85.153 +}; 85.154 + 85.155 +// Arika is based on Sega but with a few wall kicks. 85.156 +// Each free-space kick should be followed by Right, then Left 85.157 +// but for J, L, and T, kicks to vertical positions (point-right and 85.158 +// point-left) 85.159 +// T when rotating to point-up can also floor kick by one, 85.160 +// and I when rotating to vertical can floor kick by one or two. 85.161 +static const LJRotSystem rotArika = { 85.162 + .colorScheme = 1, 85.163 + .entryOffset = { 85.164 + WK( 0, 0),WK( 0, 1),WK( 0, 1),WK( 0, 0),WK( 0, 0),WK( 0, 1),WK( 0, 0), 85.165 + WK( 0, 0),WK( 0, 0),WK( 0, 0) 85.166 + }, 85.167 + .entryTheta = { 0, 2, 2, 0, 0, 2, 0, 0, 0, 2 }, 85.168 + .kicksL = {0, 1, 1, -1, 2, 8, 3, 0, -1, -1}, 85.169 + .kicksR = {4, 5, 5, -1, 6, 9, 7, 4, -1, -1}, 85.170 + .kickTables = { 85.171 + 85.172 + // 0: I counterclockwise 85.173 + { 85.174 + { WK( 0, 0),WK_END }, // R->U 85.175 + { WK( 0,-1),WK( 0, 0),WK( 0, 1),WK_END }, // D->R 85.176 + { WK(-1, 1),WK_END }, // L->D 85.177 + { WK( 1, 0),WK( 1, 1),WK( 1, 2),WK_END } // U->L 85.178 + }, 85.179 + 85.180 + // 1: JL counterclockwise 85.181 + { 85.182 + { WK( 0,-1),WK( 1,-1),WK(-1,-1),WK_END }, // R->U 85.183 + { WK( 0, 0),ARIKA_IF_NOT_CENTER,WK( 1, 0),WK(-1, 0),WK_END }, // D->R 85.184 + { WK( 0, 0),WK( 1, 0),WK(-1, 0),WK_END }, // L->D 85.185 + { WK( 0, 1),ARIKA_IF_NOT_CENTER,WK( 1, 1),WK(-1, 1),WK_END } // U->L 85.186 + }, 85.187 + 85.188 + // 2: S counterclockwise (round left, like Game Boy with WK) 85.189 + { 85.190 + { WK( 1,-1),WK( 2,-1),WK( 0,-1),WK_END }, // R->U 85.191 + { WK(-1, 0),WK( 0, 0),WK(-2, 0),WK_END }, // D->R 85.192 + { WK( 0, 0),WK( 1, 0),WK(-1, 0),WK_END }, // L->D 85.193 + { WK( 0, 1),WK( 1, 1),WK(-1, 1),WK_END } // U->L 85.194 + }, 85.195 + 85.196 + // 3: Z counterclockwise (round right, like NES with WK) 85.197 + { 85.198 + { WK( 0,-1),WK( 1,-1),WK(-1,-1),WK_END }, // R->U 85.199 + { WK( 0, 0),WK( 1, 0),WK(-1, 0),WK_END }, // D->R 85.200 + { WK(-1, 0),WK( 0, 0),WK(-2, 0),WK_END }, // L->D 85.201 + { WK( 1, 1),WK( 2, 1),WK( 0, 1),WK_END } // U->L 85.202 + }, 85.203 + 85.204 + // 4: I clockwise 85.205 + { 85.206 + { WK(-1, 0),WK_END }, // L->U 85.207 + { WK( 0, 0),WK( 0, 1),WK( 0, 2),WK_END }, // U->R 85.208 + { WK( 0, 1),WK_END }, // R->D 85.209 + { WK( 1,-1),WK( 1, 0),WK( 1, 1),WK_END } // D->L 85.210 + }, 85.211 + 85.212 + // 5: JLT clockwise 85.213 + { 85.214 + { WK( 0,-1),WK( 1,-1),WK(-1,-1),WK_END }, // L->U 85.215 + { WK( 0, 1),ARIKA_IF_NOT_CENTER,WK( 1, 1),WK(-1, 1),WK_END }, // U->R 85.216 + { WK( 0, 0),WK( 1, 0),WK(-1, 0),WK_END }, // R->D 85.217 + { WK( 0, 0),ARIKA_IF_NOT_CENTER,WK( 1, 0),WK(-1, 0),WK_END } // D->L 85.218 + }, 85.219 + 85.220 + // 6: S clockwise (round left) 85.221 + { 85.222 + { WK( 0,-1),WK( 1,-1),WK(-1,-1),WK_END }, // L->U 85.223 + { WK(-1, 1),WK( 0, 1),WK(-2, 1),WK_END }, // U->R 85.224 + { WK( 1, 0),WK( 2, 0),WK( 0, 0),WK_END }, // R->D 85.225 + { WK( 0, 0),WK( 1, 0),WK(-1, 0),WK_END } // D->L 85.226 + }, 85.227 + 85.228 + // 7: Z clockwise (round right) 85.229 + { 85.230 + { WK(-1,-1),WK( 0,-1),WK(-2,-1),WK_END }, // L->U 85.231 + { WK( 0, 1),WK( 1, 1),WK(-1, 1),WK_END }, // U->R 85.232 + { WK( 0, 0),WK( 1, 0),WK(-1, 0),WK_END }, // R->D 85.233 + { WK( 1, 0),WK( 2, 0),WK( 0, 0),WK_END } // D->L 85.234 + }, 85.235 + 85.236 + // 8: T counterclockwise (with TI floorkick) 85.237 + { 85.238 + { WK( 0,-1),WK( 1,-1),WK(-1,-1),WK( 0, 0),WK_END }, // R->U 85.239 + { WK( 0, 0),ARIKA_IF_NOT_CENTER,WK( 1, 0),WK(-1, 0),WK_END }, // D->R 85.240 + { WK( 0, 0),WK( 1, 0),WK(-1, 0),WK_END }, // L->D 85.241 + { WK( 0, 1),ARIKA_IF_NOT_CENTER,WK( 1, 1),WK(-1, 1),WK_END } // U->L 85.242 + }, 85.243 + 85.244 + // 9: T clockwise (with TI floorkick) 85.245 + { 85.246 + { WK( 0,-1),WK( 1,-1),WK(-1,-1),WK( 0, 0),WK_END }, // L->U 85.247 + { WK( 0, 1),ARIKA_IF_NOT_CENTER,WK( 1, 1),WK(-1, 1),WK_END }, // U->R 85.248 + { WK( 0, 0),WK( 1, 0),WK(-1, 0),WK_END }, // R->D 85.249 + { WK( 0, 0),ARIKA_IF_NOT_CENTER,WK( 1, 0),WK(-1, 0),WK_END } // D->L 85.250 + } 85.251 + } 85.252 +}; 85.253 + 85.254 +// All pieces are started with their left side in column 5 and flat side up. 85.255 +// All pieces stick to the top of the bounding box. 85.256 +// All 3-wide pieces stick to the left side of the bounding box. 85.257 +// I sticks to the top when left and right and occupies the second column 85.258 +// when up and down. 85.259 +// Try here, then try kicking one space left. Discovered by zaphod77: 85.260 +// http://www.tetrisconcept.com/forum/viewtopic.php?t=877 85.261 +static const LJRotSystem rotTengen = { 85.262 + .colorScheme = 1, 85.263 + .entryOffset = { 85.264 + WK( 1, 0),WK( 1, 1),WK( 1, 1),WK( 0, 0),WK( 1, 0),WK( 1, 1),WK( 1, 0), 85.265 + WK( 0, 0),WK( 1, 0),WK( 0, 0) 85.266 + }, 85.267 + .entryTheta = { 0, 2, 2, 0, 0, 2, 0, 0, 0, 2 }, 85.268 + .kicksL = {0, 3, 3, -1, 3, 3, 3, 0, 2, 5}, 85.269 + .kicksR = {1, 4, 4, -1, 4, 4, 4, 1, 3, 5}, 85.270 + .kickTables = { 85.271 + 85.272 + // 0: I counterclockwise 85.273 + { 85.274 + { WK( 1, 1),WK( 0, 1),WK_END }, // R->U 85.275 + { WK(-1,-2),WK(-2,-2),WK_END }, // D->R 85.276 + { WK( 0, 2),WK(-1, 2),WK_END }, // L->D 85.277 + { WK( 0,-1),WK(-1,-1),WK_END } // U->L 85.278 + }, 85.279 + 85.280 + // 1: I clockwise 85.281 + { 85.282 + { WK( 0, 1),WK(-1, 1),WK_END }, // L->U 85.283 + { WK(-1,-1),WK(-2,-1),WK_END }, // U->R 85.284 + { WK( 1, 2),WK( 0, 2),WK_END }, // R->D 85.285 + { WK( 0,-2),WK(-1,-2),WK_END } // D->L 85.286 + }, 85.287 + 85.288 + // 2: I3 85.289 + { 85.290 + { WK( 0, 1),WK(-1, 1),WK_END }, // L->U 85.291 + { WK( 0,-1),WK(-1,-1),WK_END }, // U->R 85.292 + { WK( 0, 1),WK(-1, 1),WK_END }, // R->D 85.293 + { WK( 0,-1),WK(-1,-1),WK_END } // D->L 85.294 + }, 85.295 + 85.296 + // 3: JLSTZ counterclockwise 85.297 + { 85.298 + { WK( 1, 0),WK( 0, 0),WK_END }, // R->U 85.299 + { WK(-1,-1),WK(-2,-1),WK_END }, // D->R 85.300 + { WK( 0, 1),WK(-1, 1),WK_END }, // L->D 85.301 + { WK( 0, 0),WK(-1, 0),WK_END } // U->L 85.302 + }, 85.303 + 85.304 + // 4: JLSTZ clockwise 85.305 + { 85.306 + { WK( 0, 0),WK(-1, 0),WK_END }, // L->U 85.307 + { WK(-1, 0),WK(-2, 0),WK_END }, // U->R 85.308 + { WK( 1, 1),WK( 0, 1),WK_END }, // R->D 85.309 + { WK( 0,-1),WK(-1,-1),WK_END } // D->L 85.310 + }, 85.311 + 85.312 + // 5: L3 85.313 + { 85.314 + { WK( 0, 0),WK(-1, 0),WK_END }, // L->U 85.315 + { WK(-1, 0),WK(-2, 0),WK_END }, // U->R 85.316 + { WK( 1, 1),WK( 0, 1),WK_END }, // R->D 85.317 + { WK( 0,-1),WK(-1,-1),WK_END } // D->L 85.318 + } 85.319 + } 85.320 +}; 85.321 + 85.322 +// NES: No wall kick 85.323 +// 3-wide pieces start out one block to the right 85.324 +// I, S and Z round to the right and use effective positions R and D 85.325 +static const LJRotSystem rotNES = { 85.326 + .colorScheme = 1, 85.327 + .entryOffset = { 85.328 + WK( 0, 0),WK( 1, 1),WK( 1, 1),WK( 0, 0),WK( 1, 0),WK( 1, 1),WK( 1, 0), 85.329 + WK( 0, 0),WK( 0, 0),WK( 0, 0) 85.330 + }, 85.331 + .entryTheta = { 0, 2, 2, 0, 0, 2, 0, 0, 0, 2 }, 85.332 + .kicksL = {0, -1, -1, -1, 0, -1, 0, 0, -1, -1}, 85.333 + .kicksR = {1, -1, -1, -1, 1, -1, 1, 1, -1, -1}, 85.334 + .kickTables = { 85.335 + 85.336 + // 0: counterclockwise (round right) 85.337 + { 85.338 + { WK( 0,-1),WK_END }, // R->U 85.339 + { WK( 0, 0),WK_END }, // D->R 85.340 + { WK(-1, 0),WK_END }, // L->D 85.341 + { WK( 1, 1),WK_END } // U->L 85.342 + }, 85.343 + // 1: clockwise (round right) 85.344 + { 85.345 + { WK(-1,-1),WK_END }, // L->U 85.346 + { WK( 0, 1),WK_END }, // U->R 85.347 + { WK( 0, 0),WK_END }, // R->D 85.348 + { WK( 1, 0),WK_END } // D->L 85.349 + } 85.350 + } 85.351 +}; 85.352 + 85.353 +// GB: No wall kick 85.354 +// I, S and Z round to the left and use effective positions L and D 85.355 +static const LJRotSystem rotGB = { 85.356 + .colorScheme = 1, 85.357 + .entryOffset = { 85.358 + WK( 0, 0),WK( 0, 1),WK( 0, 1),WK( 0, 0),WK( 0, 0),WK( 0, 1),WK( 0, 0), 85.359 + WK( 0, 0),WK( 0, 0),WK( 0, 0) 85.360 + }, 85.361 + .entryTheta = { 0, 2, 2, 0, 0, 2, 0, 0, 0, 2 }, 85.362 + .kicksL = {0, -1, -1, -1, 0, -1, 0, 0, -1, -1}, 85.363 + .kicksR = {1, -1, -1, -1, 1, -1, 1, 1, -1, -1}, 85.364 + .kickTables = { 85.365 + 85.366 + // 0: counterclockwise (round left) 85.367 + { 85.368 + { WK( 1,-1),WK_END }, // R->U 85.369 + { WK(-1, 0),WK_END }, // D->R 85.370 + { WK( 0, 0),WK_END }, // L->D 85.371 + { WK( 0, 1),WK_END } // U->L 85.372 + }, 85.373 + // 1: clockwise (round left) 85.374 + { 85.375 + { WK( 0,-1),WK_END }, // L->U 85.376 + { WK(-1, 1),WK_END }, // U->R 85.377 + { WK( 1, 0),WK_END }, // R->D 85.378 + { WK( 0, 0),WK_END } // D->L 85.379 + }, 85.380 + } 85.381 +}; 85.382 + 85.383 + 85.384 +// The rotation system of LOCKJAW: The Overdose (GBA) and 85.385 +// Tetramino (NES) TOD is simple. In free space it behaves like SRS. 85.386 +// If that's blocked, kick right, kick left, kick up. 85.387 +static const LJRotSystem rotTOD = { 85.388 + .kicksL = {0, 0, 0, -1, 0, 0, 0, 0, 0, 0}, 85.389 + .kicksR = {0, 0, 0, -1, 0, 0, 0, 0, 0, 0}, 85.390 + .kickTables = { 85.391 + 85.392 + // 0: JLSTZ counterclockwise 85.393 + { 85.394 + { WK( 0, 0),WK( 1, 0),WK(-1, 0),WK( 0, 1),WK_END }, // ->U 85.395 + { WK( 0, 0),WK( 1, 0),WK(-1, 0),WK( 0, 1),WK_END }, // ->R 85.396 + { WK( 0, 0),WK( 1, 0),WK(-1, 0),WK( 0, 1),WK_END }, // ->D 85.397 + { WK( 0, 0),WK( 1, 0),WK(-1, 0),WK( 0, 1),WK_END } // ->L 85.398 + }, 85.399 + 85.400 + // 1: I counterclockwise 85.401 + { 85.402 + { WK( 0, 0),WK(-1, 0),WK( 2, 0),WK(-1,-2),WK( 2, 1) }, // R->U 85.403 + { WK( 0, 0),WK( 1, 0),WK(-2, 0),WK( 1,-1),WK(-2, 1) }, // D->R 85.404 + { WK( 0, 0),WK( 1, 0),WK(-2, 0),WK(-2,-1),WK( 1, 2) }, // L->D 85.405 + { WK( 0, 0),WK(-1, 0),WK( 2, 0),WK( 2,-1),WK(-1, 2) } // U->L 85.406 + }, 85.407 + 85.408 + // 2: JLSTZ clockwise 85.409 + { 85.410 + { WK( 0, 0),WK(-1, 0),WK(-1,-1),WK( 0, 2),WK(-1, 2) }, // L->U 85.411 + { WK( 0, 0),WK(-1, 0),WK(-1, 1),WK( 0,-2),WK(-1,-2) }, // U->R 85.412 + { WK( 0, 0),WK( 1, 0),WK( 1,-1),WK( 0, 2),WK( 1, 2) }, // R->D 85.413 + { WK( 0, 0),WK( 1, 0),WK( 1, 1),WK( 0,-2),WK( 1,-2) }, // D->L 85.414 + }, 85.415 + 85.416 + // 3: I clockwise 85.417 + { 85.418 + { WK( 0, 0),WK( 1, 0),WK(-2, 0),WK( 1,-2),WK(-2, 1) }, // L->U 85.419 + { WK( 0, 0),WK( 1, 0),WK(-2, 0),WK(-2,-1),WK( 1, 2) }, // U->R 85.420 + { WK( 0, 0),WK(-1, 0),WK( 2, 0),WK( 2,-1),WK(-1, 2) }, // R->D 85.421 + { WK( 0, 0),WK(-1, 0),WK( 2, 0),WK(-1,-1),WK( 2, 1) } // D->L 85.422 + } 85.423 + } 85.424 +}; 85.425 + 85.426 +static const LJRotSystem rotTDX = { 85.427 + .entryOffset = { 85.428 + WK( 0, 0),WK( 1, 1),WK( 1, 1),WK( 0, 0),WK( 1, 0),WK( 1, 1),WK( 1, 0), 85.429 + WK( 0, 0),WK( 0, 0),WK( 0, 0) 85.430 + }, 85.431 + .entryTheta = { 0, 2, 2, 0, 0, 2, 0, 0, 0, 2 }, 85.432 + .kicksL = {0, 0, 0, -1, 0, 0, 0, 0, 0, 0}, 85.433 + .kicksR = {1, 1, 1, -1, 1, 1, 1, 1, 1, 1}, 85.434 + .kickTables = { 85.435 + 85.436 + // 0: counterclockwise 85.437 + { 85.438 + { WK( 0, 0),WK( 1,-1),WK_END }, // R->U 85.439 + { WK( 0, 0),WK(-1,-1),WK_END }, // D->R 85.440 + { WK( 0, 0),WK(-1, 1),WK_END }, // L->D 85.441 + { WK( 0, 0),WK( 1, 1),WK_END } // U->L 85.442 + }, 85.443 + 85.444 + // 1: clockwise 85.445 + { 85.446 + { WK( 0, 0),WK(-1,-1),WK_END }, // L->U 85.447 + { WK( 0, 0),WK(-1, 1),WK_END }, // U->R 85.448 + { WK( 0, 0),WK( 1, 1),WK_END }, // R->D 85.449 + { WK( 0, 0),WK( 1,-1),WK_END } // D->L 85.450 + } 85.451 + } 85.452 +}; 85.453 + 85.454 +const LJRotSystem *const rotSystems[N_ROTATION_SYSTEMS] = { 85.455 + &rotSRS, &rotSega, &rotArika, &rotTengen, 85.456 + &rotNES, &rotGB, &rotTOD, &rotTDX 85.457 +};
86.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 86.2 +++ b/tools/fontconv.c Fri Mar 13 00:39:12 2009 -0700 86.3 @@ -0,0 +1,296 @@ 86.4 +#define USE_CONSOLE 86.5 +#include <allegro.h> 86.6 +#include <stdio.h> 86.7 + 86.8 +const RGB fontPalette[6] = { 86.9 + {63, 0,63}, 86.10 + {63,63,63}, 86.11 + {42,42,42}, 86.12 + { 0, 0, 0}, 86.13 + {63, 0, 0}, 86.14 + {63,63, 0} 86.15 +}; 86.16 + 86.17 +typedef struct Rect { 86.18 + signed short l, t, r, b; 86.19 +} Rect; 86.20 + 86.21 + 86.22 +BITMAP *fontImg; 86.23 +const int cellHeight = 12; 86.24 +Rect glyphs[256]; 86.25 +unsigned short glyphDataOffsets[256]; 86.26 + 86.27 +#define MIN_GLYPH 0 86.28 +#define N_GLYPHS 256 86.29 + 86.30 +char fontdata[65536]; 86.31 +size_t fontdata_len = 0; 86.32 +size_t fontdata_shift = 0; 86.33 + 86.34 +// stats 86.35 +size_t eols = 0; 86.36 +size_t xparents = 0; 86.37 +size_t opaques = 0; 86.38 +size_t xlucents = 0; 86.39 + 86.40 +void collectStats(int c) { 86.41 + for (int y = glyphs[c].t; y < glyphs[c].b; ++y) { 86.42 + 86.43 + // Find right side of each row of glyph 86.44 + int right = glyphs[c].l; 86.45 + for (int x = glyphs[c].l; x < glyphs[c].r; ++x) { 86.46 + int c = getpixel(fontImg, x, y); 86.47 + if (c == 2 || c == 3) { 86.48 + right = x + 1; 86.49 + } 86.50 + } 86.51 + 86.52 + // Count transparent, semitransparent, and opaque 86.53 + // pixels within row 86.54 + for (int x = glyphs[c].l; x < right; ++x) { 86.55 + switch (getpixel(fontImg, x, y)) { 86.56 + case 1: 86.57 + ++xparents; 86.58 + break; 86.59 + case 2: 86.60 + ++xlucents; 86.61 + break; 86.62 + case 3: 86.63 + ++opaques; 86.64 + break; 86.65 + } 86.66 + } 86.67 + ++eols; 86.68 + } 86.69 +} 86.70 + 86.71 + 86.72 +void drawChar(int c) { 86.73 + int left = glyphs[c].l; 86.74 + int top = glyphs[c].t; 86.75 + int right = glyphs[c].r; 86.76 + int bottom = glyphs[c].b; 86.77 + if ((c & 0x1F) == 0) { 86.78 + rectfill(screen, 86.79 + 0, 288, SCREEN_W - 1, 288 + 32 * 4, 86.80 + 5); 86.81 + } 86.82 + 86.83 + int x = (c & 0x07) * (SCREEN_W / 8); 86.84 + int y = 288 + 32 * ((c & 0x18) >> 3); 86.85 + 86.86 + textprintf_ex(screen, font, x, y + bottom - top - 4, 3, -1, 86.87 + "%c=", c); 86.88 + stretch_blit(fontImg, screen, 86.89 + left, top, right - left, bottom - top, 86.90 + x + 16, y, (right - left) * 2, (bottom - top) * 2); 86.91 + if ((c & 0x1F) == 0x1F) { 86.92 + readkey(); 86.93 + } 86.94 +} 86.95 + 86.96 +// color 0: border around kernbox 86.97 +// color 1: background 86.98 +// color 2: mix bg+fg 86.99 +// color 3: foreground 86.100 +void findCharacters(void) { 86.101 + int curChar = MIN_GLYPH; 86.102 + int x = 0; 86.103 + int y = 12; 86.104 + 86.105 + for(y = 0; y < fontImg->h; y += cellHeight) { 86.106 + for (x = 0; x < fontImg->w; ++x) { 86.107 + // Skip if border pixel 86.108 + if (getpixel(fontImg, x, y) != 0) { 86.109 + 86.110 + // find left and right extents 86.111 + int left = x; 86.112 + int right; 86.113 + for (right = x; 86.114 + right < fontImg->w && getpixel(fontImg, right, y) != 0; 86.115 + ++right) { 86.116 + } 86.117 + 86.118 + // record them 86.119 + glyphs[curChar].l = left; 86.120 + glyphs[curChar].t = y; 86.121 + glyphs[curChar].r = right; 86.122 + glyphs[curChar].b = y + cellHeight; 86.123 + x = right; 86.124 + ++curChar; 86.125 + } 86.126 + } 86.127 + } 86.128 + 86.129 +#if 0 86.130 + int maxChar = curChar; 86.131 + for (curChar = MIN_GLYPH; curChar < maxChar; ++curChar) { 86.132 + collectStats(curChar); 86.133 + drawChar(curChar); 86.134 + } 86.135 +#endif 86.136 +} 86.137 + 86.138 +void displayStats(void) { 86.139 + rectfill(screen, 0, 288, 255, 479, 5); 86.140 + textprintf_ex(screen, font, 4, 298, 3, -1, 86.141 + "xparents: %d", xparents); 86.142 + textprintf_ex(screen, font, 4, 308, 3, -1, 86.143 + "xlucents: %d", xlucents); 86.144 + textprintf_ex(screen, font, 4, 318, 3, -1, 86.145 + "opaques: %d", opaques); 86.146 + textprintf_ex(screen, font, 4, 328, 3, -1, 86.147 + "end of lines: %d", eols); 86.148 + textprintf_ex(screen, font, 4, 338, 3, -1, 86.149 + "total number of bits: %d", 86.150 + (xparents + xlucents + opaques + eols + 3) * 2); 86.151 +} 86.152 + 86.153 +/* Details of the font encoding 86.154 + 86.155 +Header: 86.156 +u8 offsetToEncodingTable; 86.157 +u8 minGlyph; 86.158 +u8 nGlyphs; 86.159 +u8 fontHeight; 86.160 + 86.161 +First the encoding table (nglyphs/2 bytes): 86.162 +u16 offsetToGlyphData; 86.163 +u8 glyphWidth; 86.164 +u8 reserved; 86.165 + 86.166 +Each glyph data element consists of a stream of bytes. 86.167 +Each byte contains four 2-bit values packed little-endian: 86.168 +0: Move pen to start of next line 86.169 +1: Move pen to right 86.170 +2: Draw pixel using 50% opacity and move pen to right 86.171 +3: Draw pixel using 100% opacity and move pen to right 86.172 +The glyph data is done when "Move pen to start of next line" 86.173 +has run fontHeight times. 86.174 + 86.175 +*/ 86.176 +void compressPadByte(void) { 86.177 + if (fontdata_shift > 0) { 86.178 + fontdata_shift = 0; 86.179 + ++fontdata_len; 86.180 + } 86.181 +} 86.182 + 86.183 +void compressWriteCode(unsigned int code) { 86.184 + fontdata[fontdata_len] |= code << fontdata_shift; 86.185 + fontdata_shift += 2; 86.186 + if (fontdata_shift >= 8) { 86.187 + compressPadByte(); 86.188 + } 86.189 +} 86.190 + 86.191 +void compressGlyph(unsigned int c) { 86.192 + glyphDataOffsets[c] = fontdata_len; 86.193 + for (int y = glyphs[c].t; y < glyphs[c].b; ++y) { 86.194 + 86.195 + // Find right side of each row of glyph 86.196 + int right = glyphs[c].l; 86.197 + for (int x = glyphs[c].l; x < glyphs[c].r; ++x) { 86.198 + int c = getpixel(fontImg, x, y); 86.199 + if (c == 2 || c == 3) { 86.200 + right = x + 1; 86.201 + } 86.202 + } 86.203 + 86.204 + // Write transparent, semitransparent, and opaque 86.205 + // pixels within row 86.206 + for (int x = glyphs[c].l; x < right; ++x) { 86.207 + int code = getpixel(fontImg, x, y) & 3; 86.208 + if (code == 0) { 86.209 + code = 1; 86.210 + } 86.211 + compressWriteCode(code); 86.212 + } 86.213 + 86.214 + // Signal eol 86.215 + compressWriteCode(0); 86.216 + } 86.217 + compressPadByte(); 86.218 +} 86.219 + 86.220 +void compressFont(void) { 86.221 + unsigned int minGlyph = MIN_GLYPH; 86.222 + size_t nGlyphs = N_GLYPHS; 86.223 + for (size_t i = minGlyph; i < minGlyph + nGlyphs; ++i) { 86.224 + compressGlyph(i); 86.225 + } 86.226 +} 86.227 + 86.228 +int writeFont(const char *dstName) { 86.229 + FILE *dst = fopen(dstName, "wb"); 86.230 + unsigned int minGlyph = MIN_GLYPH; 86.231 + size_t nGlyphs = N_GLYPHS; 86.232 + size_t headerLen = 4; 86.233 + size_t glyphDataBase = nGlyphs * 4 + headerLen; 86.234 + 86.235 + if (!dst) { 86.236 + return EXIT_FAILURE; 86.237 + } 86.238 + fputc(4, dst); // offset to encoding table 86.239 + fputc(minGlyph, dst); 86.240 + fputc(nGlyphs, dst); 86.241 + fputc(cellHeight, dst); 86.242 + 86.243 + for (size_t i = minGlyph; i < minGlyph + nGlyphs; ++i) { 86.244 + size_t glyphDataOffset = glyphDataBase + glyphDataOffsets[i]; 86.245 + fputc(glyphDataOffset & 0xFF, dst); 86.246 + fputc((glyphDataOffset >> 8) & 0xFF, dst); 86.247 + fputc(glyphs[i].r - glyphs[i].l, dst); 86.248 + fputc(0, dst); 86.249 + } 86.250 + fwrite(fontdata, fontdata_len, 1, dst); 86.251 + fclose(dst); 86.252 + return 0; 86.253 +} 86.254 + 86.255 +#define WATCHING 0 86.256 + 86.257 +int main(int argc, const char *const *argv) { 86.258 + 86.259 + if (argc != 3) { 86.260 + fputs("syntax: fontconv fontbitmapname outname\n", stderr); 86.261 + return EXIT_FAILURE; 86.262 + } 86.263 + 86.264 + allegro_init(); 86.265 + install_timer(); 86.266 + 86.267 + set_color_depth(8); 86.268 +#if WATCHING 86.269 + if (set_gfx_mode(GFX_AUTODETECT_WINDOWED, 256, 480, 0, 0) < 0) { 86.270 + allegro_message("Could not open a dual screen sized window.\n"); 86.271 + return EXIT_FAILURE; 86.272 + } 86.273 + set_palette_range(fontPalette, 86.274 + 0, sizeof(fontPalette)/sizeof(fontPalette[0]) - 1, 86.275 + 1); 86.276 + clear_bitmap(screen); 86.277 + rectfill(screen, 0, 192, 255, 287, 2); 86.278 + rectfill(screen, 0, 288, 255, 479, 5); 86.279 + install_keyboard(); 86.280 +#endif 86.281 + 86.282 + fontImg = load_bitmap(argv[1], NULL); 86.283 + if (!fontImg) { 86.284 + allegro_exit(); 86.285 + fprintf(stderr, "fontconv could not load %s\n", 86.286 + argv[1]); 86.287 + return EXIT_FAILURE; 86.288 + } 86.289 + findCharacters(); 86.290 +#if WATCHING 86.291 + blit(fontImg, screen, 0, 0, 0, 0, fontImg->w, fontImg->h); 86.292 + readkey(); 86.293 + displayStats(); 86.294 + readkey(); 86.295 +#endif 86.296 + compressFont(); 86.297 + return writeFont(argv[2]); 86.298 +} END_OF_MAIN(); 86.299 +
87.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 87.2 +++ b/tools/mktables.c Fri Mar 13 00:39:12 2009 -0700 87.3 @@ -0,0 +1,88 @@ 87.4 +#include <stdlib.h> 87.5 +#include <stdio.h> 87.6 +#include <math.h> 87.7 + 87.8 +/* 87.9 +Pin Eight standard tuning defines middle C as 2093/8 Hz. 87.10 +It uses equal temperament: a semitone difference between two pitches 87.11 +is a factor of 2^(1/12) between their frequencies. 87.12 +Thus, MIDI 0 = 60, or 2093/256. 87.13 +*/ 87.14 +#define C_IN_OCTAVE(octave) \ 87.15 + ((double)((long int)2093 << octave) / 256.0) 87.16 +#define TWELFTH_ROOT_OF_TWO 1.0594630943592952645618252949463 87.17 + 87.18 +/* 87.19 +DS note frequencies for the tone generator at 8 samples per period 87.20 +are ((-0x1000000 / (n * 8))) 87.21 +*/ 87.22 +#define FREQ_TO_PSG_PERIOD(freq) \ 87.23 + ((unsigned short)(-0x1000000 / (freq * 8))) 87.24 + 87.25 + 87.26 +int writePSG(FILE *fp) { 87.27 + 87.28 + // write header 87.29 + fputs(".section .rodata\n" 87.30 + ".balign 4\n" 87.31 + ".global midi2psgFreq\n" 87.32 + "midi2psgFreq:", fp); 87.33 + 87.34 + int octave = 0; 87.35 + 87.36 + for (; octave < 2; ++octave) { 87.37 + fprintf(fp, "\n@ unsupported octave %d\n.byte ", octave); 87.38 + for (int semitone = 0; semitone < 12; ++semitone) { 87.39 + if (semitone) { 87.40 + fputs(", ", fp); 87.41 + } 87.42 + fputs(" 0, 0", fp); 87.43 + } 87.44 + } 87.45 + for (; octave < 10; ++octave) { 87.46 + double freq = C_IN_OCTAVE(octave); 87.47 + fprintf(fp, "\n@ octave %d\n.byte ", octave); 87.48 + for (int semitone = 0; semitone < 12; ++semitone) { 87.49 + if (semitone) { 87.50 + fputs(", ", fp); 87.51 + freq *= TWELFTH_ROOT_OF_TWO; 87.52 + } 87.53 + unsigned int psgFreq = FREQ_TO_PSG_PERIOD(freq); 87.54 + printf("midi %2d = %.2f = psg %5d\n", 87.55 + octave * 12 + semitone, 87.56 + freq, 87.57 + psgFreq); 87.58 + fprintf(fp, "%3u,%3u", 87.59 + psgFreq & 0xFF, 87.60 + (psgFreq >> 8) & 0xFF); 87.61 + } 87.62 + } 87.63 + 87.64 + fputs("\n", fp); 87.65 + return 0; 87.66 +} 87.67 + 87.68 +/** 87.69 + * Writes lookup tables used by the ARM7 code. 87.70 + * Currently, these include the PSG pitch values. 87.71 + */ 87.72 +int writeARM7(const char *filename) { 87.73 + FILE *fp = fopen(filename, "wt"); 87.74 + if (!fp) { 87.75 + return -1; 87.76 + } 87.77 + if (writePSG(fp) < 0) { 87.78 + fclose(fp); 87.79 + return -1; 87.80 + } 87.81 + fclose(fp); 87.82 + return 0; 87.83 +} 87.84 + 87.85 + 87.86 +int main(void) { 87.87 + if (writeARM7("obj/ds/lookup_tables7.s") < 0) { 87.88 + return EXIT_FAILURE; 87.89 + } 87.90 + return EXIT_SUCCESS; 87.91 +}
88.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 88.2 +++ b/zip.in Fri Mar 13 00:39:12 2009 -0700 88.3 @@ -0,0 +1,88 @@ 88.4 +src/debrief.c 88.5 +src/dsarm7.c 88.6 +src/dsdebrief.c 88.7 +src/dsjoy.c 88.8 +src/dssound7.c 88.9 +src/dssleep.c 88.10 +src/explode.c 88.11 +src/font.bmp 88.12 +src/fontdraw.c 88.13 +src/fontdraw_engine.c 88.14 +src/fontdraw.iwram.c 88.15 +src/fontdraw.h 88.16 +src/gba_asm.s 88.17 +src/gbablk.chr 88.18 +src/gbaisr.iwram.c 88.19 +src/gbamenus.c 88.20 +src/gbamenus.h 88.21 +src/gbanotefreq.c 88.22 +src/gbaopt.c 88.23 +src/gbasound.c 88.24 +src/gimmicks.c 88.25 +src/lj.c 88.26 +src/lj.h 88.27 +src/ljcontrol.h 88.28 +src/ljds.c 88.29 +src/ljds.h 88.30 +src/ljgba.c 88.31 +src/ljgba.h 88.32 +src/ljgbads.inc 88.33 +src/ljlocale.c 88.34 +src/ljlocale.h 88.35 +src/ljmusic.c 88.36 +src/ljmusic.h 88.37 +src/ljpath.c 88.38 +src/ljpath.h 88.39 +src/ljpc.c 88.40 +src/ljpc.h 88.41 +src/ljplay.c 88.42 +src/ljplay.h 88.43 +src/ljreplay.c 88.44 +src/ljreplay.h 88.45 +src/ljtypes.h 88.46 +src/ljvorbis.c 88.47 +src/ljvorbis.h 88.48 +src/macro.c 88.49 +src/old_pc_options.c 88.50 +src/options.c 88.51 +src/options.h 88.52 +src/pcdebrief.c 88.53 +src/pcjoy.c 88.54 +src/pcjoy.h 88.55 +src/pcsound.c 88.56 +src/pin8gba_sound.h 88.57 +src/random.c 88.58 +src/scenario.c 88.59 +src/scenario.h 88.60 +src/speed.c 88.61 +src/talkback.h 88.62 +src/text.chr 88.63 +src/winicon.rc 88.64 +src/wktables.c 88.65 +docs/favicon.ico 88.66 +docs/ljlogo192.png 88.67 +docs/titlebarbg.png 88.68 +docs/ljsnap018.png 88.69 +docs/ijlo-stz.png 88.70 +docs/appicon.ico 88.71 +docs/dsicon.bmp 88.72 +docs/appicon.xcf 88.73 +docs/ljhtml.css 88.74 +docs/Compiling_on_Windows.txt 88.75 +obj/win32/index.txt 88.76 +obj/gba/index.txt 88.77 +obj/ds/index.txt 88.78 +tools/fontconv.c 88.79 +tools/mktables.c 88.80 +makefile 88.81 +gbamakefile 88.82 +dsmakefile 88.83 +COPYING.txt 88.84 +GPL.txt 88.85 +zip.in 88.86 +binzip.in 88.87 +mkzip.bat 88.88 +README.html 88.89 +CHANGES.txt 88.90 +TODO.txt 88.91 +BUGS.txt