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