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