1 /*
2 * nazghul - an old-school RPG engine
3 * Copyright (C) 2004 Gordon McNutt
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Foundation, Inc., 59 Temple Place,
17 * Suite 330, Boston, MA 02111-1307 USA
18 *
19 * Gordon McNutt
20 * gmcnutt@users.sourceforge.net
21 *
22 */
23
24 #include "magic.h"
25 #include "debug.h"
26
27 #include <ctype.h>
28 #include <stdlib.h>
29 #include <string.h>
30
31 /*****************************************************************************
32 *
33 * spell api
34 *
35 *****************************************************************************/
36
spell_new(char * code)37 static struct spell *spell_new(char *code)
38 {
39 struct spell *spell;
40
41 spell = (struct spell*)calloc(1, sizeof(*spell));
42 assert(spell);
43
44 spell->code = strdup(code);
45 assert(spell->code);
46
47 return spell;
48 }
49
spell_del(struct spell * entry)50 static void spell_del(struct spell *entry)
51 {
52 assert(entry);
53
54 if (entry->code)
55 free(entry->code);
56 free(entry);
57 }
58
spell_add_reagent(struct spell * spell,ObjectType * reagent)59 int spell_add_reagent(struct spell *spell, ObjectType *reagent)
60 {
61 if (spell->n_reagents == MAX_MIX_REAGENTS) {
62 warn("spell_add_reagent: spell %s already maxed out on "\
63 "reagents\n", spell->code);
64 return -1;
65 }
66
67 spell->reagents[spell->n_reagents++] = reagent;
68 return 0;
69 }
70
71 /*****************************************************************************
72 *
73 * helper functions
74 *
75 *****************************************************************************/
76
magic_check_code(struct magic * magic,char * code)77 static int magic_check_code(struct magic *magic, char *code)
78 {
79 if (! code)
80 return -1;
81
82 while (*code) {
83 if (! magic->words[(*code - 'A')]) {
84 warn("magic_check_code: no magic word starts with %c\n",
85 *code);
86 return -1;
87 }
88 code++;
89 }
90
91 return 0;
92 }
93
magic_del_spell_tree(struct spell * root)94 static void magic_del_spell_tree(struct spell *root)
95 {
96 if (root->left)
97 magic_del_spell_tree(root->left);
98 if (root->right)
99 magic_del_spell_tree(root->right);
100 spell_del(root);
101 }
102
magic_is_code(char code)103 static int magic_is_code(char code)
104 {
105 int index = code - 'A';
106 return (index >= 0 && index < MAX_SPELL_WORDS);
107 }
108
109 /*****************************************************************************
110 *
111 * public api
112 *
113 *****************************************************************************/
114
115
116 /* Initialize at start-of-session prior to loading */
magic_init(struct magic * magic)117 void magic_init(struct magic *magic)
118 {
119 memset(magic->words, 0, sizeof(magic->words));
120 magic->spells = NULL;
121 }
122
123 /* Cleanup at end-of-session */
magic_end_session(struct magic * magic)124 void magic_end_session(struct magic *magic)
125 {
126 int i;
127
128 for (i = 0; i < MAX_SPELL_WORDS; i++) {
129 if (NULL != magic->words[i])
130 free(magic->words[i]);
131 magic->words[i] = NULL;
132 }
133
134 if (magic->spells != NULL) {
135 magic_del_spell_tree(magic->spells);
136 magic->spells = NULL;
137 }
138 }
139
magic_add_spell(struct magic * magic,char * code)140 struct spell *magic_add_spell(struct magic *magic, char *code)
141 {
142 struct spell *parent = 0;
143 struct spell *current = magic->spells;
144 int dir;
145
146 /* Make sure the code is valid */
147 if (magic_check_code(magic, code))
148 return NULL;
149
150 /* Descend the spell tree to a vacant leaf */
151 while (current) {
152
153 /* Parent always points to last valid node */
154 parent = current;
155
156 dir = strcmp(code, current->code);
157
158 /* Check if a spell was already inserted with this code */
159 if (dir == 0) {
160 warn("magic_add_spell: there's already a spell with "\
161 "code '%s'\n", code);
162 return NULL;
163 }
164 else if (dir < 0)
165 current = current->left;
166 else
167 current = current->right;
168 }
169
170 current = spell_new(code);
171
172 if (!parent) {
173 /* First entry (root) */
174 magic->spells = current;
175 } else if (strcmp(code, parent->code) < 0) {
176 /* Left child vacant */
177 parent->left = current;
178 } else {
179 /* Right child vacant */
180 parent->right = current;
181 }
182
183 /* Success */
184 return current;
185
186 }
187
188 /* Lookup spells when casting them during play */
magic_lookup_spell(struct magic * magic,char * code)189 struct spell *magic_lookup_spell(struct magic *magic, char *code)
190 {
191 struct spell *current;
192
193 current = magic->spells;
194
195 while (current) {
196
197 int dir = strcmp(code, current->code);
198
199 if (! dir) {
200 /* Found it! */
201 return current;
202 } else if (dir < 0) {
203 current = current->left;
204 } else {
205 current = current->right;
206 }
207 }
208
209 /* Didn't find it */
210 return NULL;
211 }
212
magic_add_word(struct magic * magic,char * word)213 int magic_add_word(struct magic *magic, char *word)
214 {
215 int index = word[0] - 'A';
216
217 if (! magic_is_code(word[0])) {
218 warn("magic_add_word: letter '%c' out of range [A, %c]\n",
219 word[0], MAX_SPELL_WORDS + 'A' - 1);
220 return -1;
221 }
222
223 if (magic->words[index]) {
224 warn("magic_add_word: cannot add word '%s' because word "\
225 "'%s' already starts with '%c'\n", word,
226 magic->words[index],
227 word[0]);
228 return -1;
229 }
230
231 magic->words[index] = strdup(word);
232 assert(magic->words[index]);
233
234 return 0;
235 }
236
magic_lookup_word(struct magic * magic,char first_letter)237 char *magic_lookup_word(struct magic *magic, char first_letter)
238 {
239 int index = first_letter - 'A';
240
241 if (! magic_is_code(first_letter)) {
242 warn("magic_lookup_word: letter '%c' out of range [A, %c]\n",
243 first_letter, MAX_SPELL_WORDS + 'A' - 1);
244 return NULL;
245 }
246
247 return magic->words[index];
248 }
249
magic_spell_code_to_name(struct magic * magic,char * buf,int len,char * code)250 int magic_spell_code_to_name(struct magic *magic, char *buf, int len,
251 char *code)
252 {
253 int n = 0;
254
255 while (*code) {
256 char *word = magic_lookup_word(magic, *code);
257 if (! word)
258 return -1;
259 n = snprintf(buf, len, "%s ", word);
260 if (n < 0)
261 return -1;
262 buf += n;
263 len -= n;
264 code++;
265 }
266
267 // back up over the last space
268 if (! *code) {
269 buf--;
270 *buf = 0;
271 }
272
273 return 0;
274 }
275
magic_spell_name_to_code(struct magic * magic,char * code,int len,const char * name)276 int magic_spell_name_to_code(struct magic *magic, char *code, int len,
277 const char *name)
278 {
279 int state = 2, i = 0;
280 const char *ptr = name;
281
282 len--; /* leave space for null terminator */
283
284 while (*ptr && len) {
285 char c = *ptr;
286 ptr++;
287 switch (state) {
288 case 0:
289 /* looking for start of next word */
290 if (! isspace(c)) {
291
292 /* If this is a valid word then store it's
293 * first letter in the code buffer, else ignore
294 * it. Ignoring it lets us handle things like
295 * "vas Flam spell", where we ignore the
296 * "spell" part. */
297 if (magic_is_code(c)) {
298 code[i] = c;
299 i++;
300 len--;
301 }
302 state = 1;
303 }
304 break;
305 case 1:
306 /* looking for end of current word */
307 if (isspace(c)) {
308 state = 0;
309 }
310 break;
311 case 2:
312 /* spell names have code in <> */
313 if (c == '<') {
314 state = 0;
315 }
316 break;
317 default:
318 assert(0);
319 break;
320 }
321 }
322
323 code[i] = 0; /* null-terminate */
324
325 return 0;
326 }
327