1 //********************************************************************************************
2 //*
3 //* This file is part of Egoboo.
4 //*
5 //* Egoboo is free software: you can redistribute it and/or modify it
6 //* under the terms of the GNU General Public License as published by
7 //* the Free Software Foundation, either version 3 of the License, or
8 //* (at your option) any later version.
9 //*
10 //* Egoboo is distributed in the hope that it will be useful, but
11 //* WITHOUT ANY WARRANTY; without even the implied warranty of
12 //* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 //* General Public License for more details.
14 //*
15 //* You should have received a copy of the GNU General Public License
16 //* along with Egoboo. If not, see <http://www.gnu.org/licenses/>.
17 //*
18 //********************************************************************************************
19
20 /// @file quest.c
21 /// @brief Handles functions that modify quest.txt files and the players quest log
22 /// @details ZF@> This could be done more optimal with a proper HashMap allowing O(1) speed instead of O(n)
23 /// I think we should also implement a similar system for skill IDSZ.
24
25 #include "IDSZ_map.h"
26 #include "quest.h"
27 #include "log.h"
28
29 #include "egoboo_fileutil.h"
30 #include "egoboo_vfs.h"
31 #include "egoboo.h"
32
33 #include "egoboo_math.inl"
34
35 //--------------------------------------------------------------------------------------------
36 //--------------------------------------------------------------------------------------------
quest_file_open(const char * player_directory)37 ConfigFilePtr_t quest_file_open( const char *player_directory )
38 {
39 STRING newloadname = EMPTY_CSTR;
40 ConfigFilePtr_t retval = NULL;
41
42 if ( !VALID_CSTR( player_directory ) ) return NULL;
43
44 // Figure out the file path
45 snprintf( newloadname, SDL_arraysize( newloadname ), "%s/quest.txt", player_directory );
46
47 retval = LoadConfigFile( newloadname, bfalse );
48 if ( NULL == retval )
49 {
50 retval = ConfigFile_create();
51 }
52
53 return retval;
54 }
55
56 //--------------------------------------------------------------------------------------------
quest_file_export(ConfigFilePtr_t pfile)57 egoboo_rv quest_file_export( ConfigFilePtr_t pfile )
58 {
59 egoboo_rv rv = rv_error;
60 ConfigFile_retval save_rv = ConfigFile_succeed;
61
62 if ( NULL == pfile ) return rv_error;
63
64 save_rv = SaveConfigFile( pfile );
65
66 rv = ( ConfigFile_succeed == save_rv ) ? rv_success : rv_fail;
67
68 return rv;
69 }
70
71 //--------------------------------------------------------------------------------------------
quest_file_close(ConfigFilePtr_t * ppfile,bool_t export)72 egoboo_rv quest_file_close( ConfigFilePtr_t * ppfile, bool_t export )
73 {
74 egoboo_rv export_rv = rv_success;
75
76 if ( NULL == ppfile || NULL == *ppfile ) return rv_error;
77
78 if ( export )
79 {
80 export_rv = quest_file_export( *ppfile );
81
82 if ( rv_error == export_rv )
83 {
84 log_warning( "quest_file_close() - error writing quest.txt\n" );
85 }
86 else if ( rv_fail == export_rv )
87 {
88 log_warning( "quest_file_close() - could not export quest.txt\n" );
89 }
90 }
91
92 if ( ConfigFile_succeed != ConfigFile_destroy( ppfile ) )
93 {
94 log_warning( "quest_file_close() - could not successfully close quest.txt\n" );
95 }
96
97 return ( NULL == *ppfile ) && ( rv_success == export_rv ) ? rv_success : rv_fail;
98 }
99
100 //--------------------------------------------------------------------------------------------
101 //--------------------------------------------------------------------------------------------
quest_log_download_vfs(IDSZ_node_t quest_log[],size_t quest_log_len,const char * player_directory)102 egoboo_rv quest_log_download_vfs( IDSZ_node_t quest_log[], size_t quest_log_len, const char* player_directory )
103 {
104 /// @details ZF@> Reads a quest.txt for a player and turns it into a data structure
105 /// we can use. If the file isn't found, the quest log will be initialized as empty.
106
107 egoboo_rv retval = rv_success;
108 vfs_FILE *fileread;
109 STRING newloadname;
110
111 if ( quest_log == NULL ) return rv_error;
112
113 // blank out the existing map
114 idsz_map_init( quest_log, quest_log_len );
115
116 // Figure out the file path
117 snprintf( newloadname, SDL_arraysize( newloadname ), "%s/quest.txt", player_directory );
118
119 // try to open the file
120 fileread = vfs_openRead( newloadname );
121 if ( NULL == fileread ) return rv_success;
122
123 // Load each IDSZ
124 retval = rv_success;
125 while ( goto_colon( NULL, fileread, btrue ) )
126 {
127 egoboo_rv rv;
128
129 IDSZ idsz = fget_idsz( fileread );
130 int level = fget_int( fileread );
131
132 // Try to add a single quest to the map
133 rv = idsz_map_add( quest_log, quest_log_len, idsz, level );
134
135 // Stop here if it failed
136 if ( rv_error == rv )
137 {
138 log_warning( "quest_log_download_vfs() - Encountered an error while trying to add a quest. (%s)\n", newloadname );
139 retval = rv;
140 break;
141 }
142 else if ( rv_fail == rv )
143 {
144 log_warning( "quest_log_download_vfs() - Unable to load all quests. (%s)\n", newloadname );
145 retval = rv;
146 break;
147 }
148 }
149
150 // Close up after we are done with it
151 vfs_close( fileread );
152
153 return retval;
154 }
155
156 //--------------------------------------------------------------------------------------------
quest_log_upload_vfs(IDSZ_node_t quest_log[],size_t quest_log_len,const char * player_directory)157 egoboo_rv quest_log_upload_vfs( IDSZ_node_t quest_log[], size_t quest_log_len, const char *player_directory )
158 {
159 /// @details ZF@> This exports quest_log data into a quest.txt file
160 vfs_FILE *filewrite;
161 int iterator;
162 IDSZ_node_t *pquest;
163
164 if ( quest_log == NULL ) return rv_error;
165
166 // Write a new quest file with all the quests
167 filewrite = vfs_openWrite( player_directory );
168 if ( NULL == filewrite )
169 {
170 log_warning( "Cannot create quest file! (%s)\n", player_directory );
171 return rv_fail;
172 }
173
174 vfs_printf( filewrite, "// This file keeps order of all the quests for this player\n" );
175 vfs_printf( filewrite, "// The number after the IDSZ shows the quest level. %i means it is completed.", QUEST_BEATEN );
176
177 // Iterate through every element in the IDSZ map
178 iterator = 0;
179 pquest = idsz_map_iterate( quest_log, quest_log_len, &iterator );
180 while ( pquest != NULL )
181 {
182 // Write every single quest to the quest log
183 vfs_printf( filewrite, "\n:[%4s] %i", undo_idsz( pquest->id ), pquest->level );
184
185 // Get the next element
186 pquest = idsz_map_iterate( quest_log, quest_log_len, &iterator );
187 }
188
189 // Clean up and return
190 vfs_close( filewrite );
191 return rv_success;
192 }
193
194 //--------------------------------------------------------------------------------------------
quest_set_level(IDSZ_node_t quest_log[],size_t quest_log_len,IDSZ idsz,int level)195 int quest_set_level( IDSZ_node_t quest_log[], size_t quest_log_len, IDSZ idsz, int level )
196 {
197 ///@details ZF@> This function will set the quest level for the specified quest
198 /// and return the new quest_level. It will return QUEST_NONE if the quest was
199 /// not found.
200
201 IDSZ_node_t *pquest = NULL;
202
203 // find the quest
204 pquest = idsz_map_get( quest_log, quest_log_len, idsz );
205 if ( pquest == NULL ) return QUEST_NONE;
206
207 // make a copy of the quest's level
208 pquest->level = level;
209
210 return level;
211 }
212
213 //--------------------------------------------------------------------------------------------
quest_adjust_level(IDSZ_node_t quest_log[],size_t quest_log_len,IDSZ idsz,int adjustment)214 int quest_adjust_level( IDSZ_node_t quest_log[], size_t quest_log_len, IDSZ idsz, int adjustment )
215 {
216 ///@details ZF@> This function will modify the quest level for the specified quest with adjustment
217 /// and return the new quest_level total. It will return QUEST_NONE if the quest was
218 /// not found or if it was already beaten.
219
220 int src_level = QUEST_NONE;
221 int dst_level = QUEST_NONE;
222 IDSZ_node_t *pquest = NULL;
223
224 // find the quest
225 pquest = idsz_map_get( quest_log, quest_log_len, idsz );
226 if ( pquest == NULL ) return QUEST_NONE;
227
228 // make a copy of the quest's level
229 src_level = pquest->level;
230
231 // figure out what the dst_level is
232 if ( QUEST_BEATEN == src_level )
233 {
234 // Don't modify quests that are already beaten
235 dst_level = src_level;
236 }
237 else
238 {
239 // if the quest "doesn't exist" make the src_level 0
240 if ( QUEST_NONE == src_level ) src_level = 0;
241
242 // Modify the quest level for that specific quest
243 if ( adjustment == QUEST_MAXVAL ) dst_level = QUEST_BEATEN;
244 else dst_level = MAX( 0, src_level + adjustment );
245
246 // set the quest level
247 pquest->level = dst_level;
248 }
249
250 return dst_level;
251 }
252
253 //--------------------------------------------------------------------------------------------
quest_get_level(IDSZ_node_t quest_log[],size_t quest_log_len,IDSZ idsz)254 int quest_get_level( IDSZ_node_t quest_log[], size_t quest_log_len, IDSZ idsz )
255 {
256 ///@details ZF@> Returns the quest level for the specified quest IDSZ.
257 /// It will return QUEST_NONE if the quest was not found or if the quest was beaten.
258
259 IDSZ_node_t *pquest;
260 pquest = idsz_map_get( quest_log, quest_log_len, idsz );
261 if ( pquest == NULL ) return QUEST_NONE;
262 return pquest->level;
263 }
264
265 //--------------------------------------------------------------------------------------------
quest_add(IDSZ_node_t quest_log[],size_t quest_log_len,IDSZ idsz,int level)266 egoboo_rv quest_add( IDSZ_node_t quest_log[], size_t quest_log_len, IDSZ idsz, int level )
267 {
268 ///@details ZF@> This adds a new quest to the quest log. If the quest is already in there, the higher quest
269 /// level of either the old and new one will be kept.
270
271 return idsz_map_add( quest_log, quest_log_len, idsz, level );
272 }
273