Solitaire for the TI-84 Plus CE!
1// Kabufuda Solitaire / KBFDSLTR for the TI-84 Plus CE
2// Copyright (C) 2024 euphory
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17#include "input.h"
18
19#include <stdlib.h>
20#include <keypadc.h>
21#include "variables.h"
22#include "ops.h"
23#include "save.h"
24
25unsigned char down, left, right, up;
26
27bool doInput()
28{
29 const bool prevSecond = kb_IsDown(kb_Key2nd);
30 const bool prevEnter = kb_IsDown(kb_KeyEnter);
31 const bool prevAlpha = kb_IsDown(kb_KeyAlpha);
32 const bool prevClear = kb_IsDown(kb_KeyClear);
33
34 kb_Scan();
35
36 up = kb_IsDown(kb_KeyUp) ? up + 1 : 0;
37 down = kb_IsDown(kb_KeyDown) ? down + 1 : 0;
38 left = kb_IsDown(kb_KeyLeft) ? left + 1 : 0;
39 right = kb_IsDown(kb_KeyRight) ? right + 1 : 0;
40
41 const bool select = (kb_IsDown(kb_Key2nd) && !prevSecond) || (kb_IsDown(kb_KeyEnter) && !prevEnter);
42 const bool clear = (kb_IsDown(kb_KeyClear) && !prevClear) || (kb_IsDown(kb_KeyAlpha) && !prevAlpha);
43
44 if (select)
45 {
46 if (cursorMode == SELECT && canGrabCard())
47 {
48 orgStack = cursorStack;
49 orgIndex = cursorIndex;
50 cursorMode = DROP;
51 }
52 else if (canDropCard())
53 {
54 dropCard();
55 cursorMode = SELECT;
56 }
57 else if (cursorStack == orgStack)
58 {
59 cursorMode = SELECT;
60 }
61 }
62 else if (clear) cursorMode = SELECT;
63
64 unsigned char prevCursorStack = cursorStack;
65
66 if (down == 1 || down > HOLD_TIME)
67 {
68 cursorIndex++;
69 }
70 else if (left == 1 || left > HOLD_TIME)
71 {
72 do
73 {
74 if (cursorStack == 0) cursorStack = NUM_FREECELLS + NUM_TABLSLOTS - 1; // wrap
75 else cursorStack--;
76 }
77 while (cursorOnCollapsed() || cursorOnLocked()); // skip useless stacks
78 }
79 else if (right == 1 || right > HOLD_TIME)
80 {
81 do
82 {
83 if (cursorStack > NUM_FREECELLS + NUM_TABLSLOTS - 2) cursorStack = 0; // wrap
84 else cursorStack++;
85 }
86 while (cursorOnCollapsed() || cursorOnLocked()); // skip useless stacks
87 }
88 else if (up == 1 || up > HOLD_TIME)
89 {
90 if (cursorIndex > 0) cursorIndex--;
91 }
92
93 if (cursorStack < NUM_FREECELLS)
94 {
95 cursorIndex = 0;
96 }
97 else if (cursorMode == DROP)
98 {
99 // if dropping cards, the cursor should always be at the top of the stack it's on
100 // except maxCursorIndex() doesn't work so well on empty tableau stacks
101 if (cursorStack != orgStack)
102 {
103 if (tableau[cursorStack - NUM_FREECELLS][0] == 11)
104 {
105 cursorIndex = 0;
106 }
107 else
108 {
109 maxCursorIndex();
110 cursorIndex++;
111 }
112 }
113 else cursorIndex = orgIndex; // also this makes it more clear if we are trying to drop
114 // back on the original cards
115 }
116 else if (cursorStack != prevCursorStack)
117 {
118 maxCursorIndex();
119 }
120 else
121 {
122 while (tableau[cursorStack - NUM_FREECELLS][cursorIndex] == 11 && cursorIndex > 0) cursorIndex--;
123 }
124
125 if (cursorMode == SELECT)
126 {
127 selectedQty = 1;
128 if (cursorStack < NUM_FREECELLS) selectedCard = freeCells[cursorStack];
129 else
130 {
131 selectedCard = tableau[cursorStack - NUM_FREECELLS][cursorIndex];
132 for (int i = cursorIndex + 1; tableau[cursorStack - NUM_FREECELLS][i] == selectedCard; i++)
133 selectedQty++;
134 }
135
136 if (selectedCard > 11)
137 {
138 selectedCard -= 12;
139 selectedQty = 4;
140 }
141 }
142
143 if (kb_IsDown(kb_KeyDel))
144 {
145 deleteSave();
146 start();
147 }
148
149 return !kb_On && progress < 10;
150}