cześć próbuję jedną gierke skompilować i wywala mi taki dziwny error
1>saverestore.cpp(87): error C2466: cannot allocate an array of constant size 0
1>saverestore.cpp(118): error C2466: cannot allocate an array of constant size 0
pierwszy raz się spotykam z takim błędem
saverestore.cpp
/* copyright (c) 2009 S. Gilles <soluphobe@gmail.com>
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* this is for saving games. It is designed to make a full
* record of the level (ex/including the player, complicated) into a file on disk,
* complete with visibility and links to other levels. the actual
* data about the game is stored in another file which contains
* a link to the current world. When that file is loaded, then world
* is restored recursively. This could allow odd things, like
* the same bit of the world being used in multiple games,
* as long as the player hasn't landed on it. good?
*/
#include "saverestore.h"
//this gets all the nice #define's in there.
#include "event.h"
#include "constants.h"
#include "worldlet.h"
#include "usr_cntrl_actor.h"
#include "item.h"
#include "monster.h"
#include <string>
#include <vector>
#include <fstream>
#include <istream>
#include <stdlib.h>
#include <sstream>
#include <sys/stat.h>
extern void tell(const char *info); //in main.cpp - grumble
#define GET_ACTOR_VAR(x); {getline(i,a_stmp,LS_SEP); atmp->x = ival(a_stmp); }
#define GET_PLAYER_VAR(x); {getline(i,a_stmp,LS_SEP); player->x = ival(a_stmp);}
#define GET_ACTOR_IPOINTER(x); {getline(i, a_stmp,LS_SEP); if (a_stmp==s(LS_NUL_INDEX)){atmp->x=NULL; } else { atmp->x=(*l_i_table)[ival(a_stmp)]; }}
#define GET_PLAYER_IPOINTER(x); {getline(i, a_stmp,LS_SEP); if (a_stmp==s(LS_NUL_INDEX)){player->x=NULL;} else { player->x=(*l_i_table)[ival(a_stmp)];}}
//the ';' is there because the way it is defined, the macro looks like a function
//and emacs insists on being upset unless I 'properly' end the 'function'
/**********************function identifiers*****************************/
//when you create a function that is designed to be used as event, you must register it in the appropriate function.
//it's a pain in the neck, but it's the only portable way to have save files work correctly
void (*f_voids[])() =
{
&standard_health_restore,
&gen_monster_r,
};
int id_of_function(void (*t_f)())
{
for (unsigned int x=0; x<(sizeof f_voids)/(sizeof f_voids[0]); ++x){
if (f_voids[x]==t_f){
return x;
}
}
return -1;
}
void(*f_items[])(item *) =
{
//???
};
int id_of_function(void (*t_f)(item *))
{
for (unsigned int x=0; x<(sizeof f_items)/(sizeof f_items[0]); ++x){
if (f_items[x]==t_f){
return x;
}
}
return -1;
}
void (*f_actors[])(actor *) = {
&poison_damage_low,
&poison_damage_mid,
&poison_damage_high,
};
int id_of_function(void (*t_f)(actor *))
{
for (unsigned int x=0; x<(sizeof f_actors)/(sizeof f_actors[0]); ++x){
if (f_actors[x]==t_f){
return x;
}
}
return -1;
}
void (*f_grounds[])(ground *) =
{
//??? don't have any functions for this yet
};
int id_of_function(void (*t_f)(ground *))
{
return -1;
}
void (*f_int_int_items[])(int, int, item *) = {
&zombificate_corpse,
};
int id_of_function(void (*t_f)(int, int, item *))
{
for (unsigned int x=0; x<(sizeof f_int_int_items)/(sizeof f_int_int_items[0]); ++x){
if (f_int_int_items[x]==t_f){
return x;
}
}
return -1;;
}
void (*f_int_int_actor_item_grounds[])(int, int, actor *, item *, ground *) = {
&modify_stat,
};
int id_of_function(void (*t_f)(int, int, actor*, item*, ground*))
{
for (unsigned int x=0; x<(sizeof f_int_int_actor_item_grounds)/(sizeof f_int_int_actor_item_grounds[0]); ++x){
if (f_int_int_actor_item_grounds[x]==t_f){
return x;
}
}
return -1;;
}
//as opposed to the above, which give general indecies of universal functions,
//these functions give local indicies of
//yes, yes, this should be done with templates.
int index_of(item *tmp, std::vector<item *> *table){
for (unsigned int x=0; x<table->size(); ++x){
if ((*table)[x]==tmp){ return x; }
}
return LS_NUL_INDEX;
}
int index_of(ground *tmp, std::vector<ground *> *table){
for (unsigned int x=0; x<table->size(); ++x){
if ((*table)[x]==tmp){ return x; }
}
return LS_NUL_INDEX;
}
int index_of(actor *tmp, std::vector<actor *> *table){
if (is_player(tmp)){ return LS_PLAYER_INDEX; }
for (unsigned int x=0; x<table->size(); ++x){
if ((*table)[x]==tmp){ return x; }
}
return LS_NUL_INDEX;
}
/***********************************************************************/
//because each dumplevel() function populates tables for
//actors, ground, and items, a master list is needed when the events
//which are universal, try and access those events. So there
//are three vectors of pointers to vectors here, which will eventually
//contain records of ALL items, grounds, actors, etc.
std::vector<std::vector<item *> *> all_i_table;
std::vector<std::vector<ground *> *> all_g_table;
std::vector<std::vector<actor *> *> all_a_table;
std::vector<worldlet *> all_w_table; //allows world-id's to be generated that match the id's in tables.
//tables for loading are needed too (pointer's, etc). Unfortunately, they can't
//be the same as the above four tables because when a game is loaded, these tables
//become semi-permanent storage for things that existed in previous sessions.
//they must remain clean and existant. If the two tables overlap, bad things will
//happen when a games is started->saved[quit]loaded->saved again, up to and including
//SEGFAULTS and complete loss/scrambling of data from different erae.
std::vector<std::vector<item *> *> l_all_i_table;
std::vector<std::vector<ground *> *> l_all_g_table;
std::vector<std::vector<actor *> *> l_all_a_table;
std::vector<worldlet *> l_all_w_table;
//now, the functions that ACTUALLY save the game
//each of these functions should be heavily documented. The dumpXXXX ones are underoptimized (templates should be used)
//but reading all of them should provide insight.
void dumpgame()
{
std::string name=player->name+".sav";
std::ofstream o(name.c_str());
//clear the globals... It might be that there can be multiple saves per session.
all_i_table.clear();
all_g_table.clear();
all_a_table.clear();
all_w_table.clear();
//first, dump all levels
dumplevel(current_world);
//store version and current world number
o<<VERSION<<LS_SEP<<"l."+player->name+"."+s(current_world->level_number)+"."+s(current_world->id)+".lvl"<<LS_SEP;
o<<action_timer<<LS_SEP<<player->x<<LS_SEP<<player->y<<LS_SEP; //store current time, player x, player y.
//todo: insert happenings here. Store them in the form:
for (unsigned int q=0; q<new_happenings.size(); ++q){
happenings.push_back(new_happenings[q]); //make sure all happenings are up-to-date
}
std::vector<event *>::iterator etmp=happenings.begin();
while(etmp!=happenings.end()){
dumpevent(&o, *etmp);
etmp++;
}
o<<LS_NUL;
//don't dump player's data, he is now dumped in with regular monsters as a special index
}
void dumplevel(worldlet *wtmp)
{
//first, get level name (uninspired)
//l.<playername>.<depth>.<id>.lvl
//first dump VERSION;depth;lighting;X_SIZE;Y_SIZE
//dump all ground:
// TYPE;item;item;item...NULL;<name_of_connected_world>
//the next name can be calculated easily, so it's okay.
//dump all actors;
//stat;stat;stat;stat...;stat;item;item...NULL
//there are a lot of strange pointers used, mostly in happenings.
//to expedite recreating them, a table of item/ground/actor pointers
//is embedded in the file. At the start of i/g/r definition, an index,
//enclosed in LS_MARK is placed. When restoring this file, a table of
//actors, ground, whatever is built up. Happenings store their arguments as
//these indecies.
//TODO: (Like, really soon. If you're reading this, you should probably email soluphobe NOW)
//note that happenings can (and should, in a lot of cases) contain pointers to objects
//and places on other levels. To get around this, happenings should store their arguments as
//a reference to the level where the thing is stored, and it's index there. This means that there will
//need to be a 2D vector of items reconstructed as the game loads - one vector per level, holding indicies
//the whole thing should probably be encrypted too. yeah. Try a Vinagere XOR cipher - like ROT13 but harder to read.
if (wtmp==NULL){ return; } //wtf?
for (unsigned int x=0; x<all_w_table.size(); ++x){
if (wtmp==all_w_table[x]){ return; } //dumping level that has/is already been/being dumped
}
std::string lv_name="l."+player->name+"."+s(wtmp->level_number)+"."+s(wtmp->id)+".lvl";
std::ofstream o(lv_name.c_str());
//basic information
o<<VERSION<<LS_SEP<<wtmp->level_number<<LS_SEP<<wtmp->id;
o<<LS_SEP<<wtmp->lighting<<LS_SEP<<X_SIZE<<LS_SEP<<Y_SIZE<<LS_SEP;
all_w_table.push_back(wtmp);
//items... The dumpitem function is called by dumpground and dumpactor, but the table must
//be set up ahead of time
std::vector<item *> *all_item = new std::vector<item *>();
all_i_table.push_back(all_item);
all_item->reserve(X_SIZE*Y_SIZE); //this is probably a good approximation of the number of items in the level, no?
//ground
std::vector<ground *> *all_ground = new std::vector<ground *>();
all_g_table.push_back(all_ground);
all_ground->reserve(X_SIZE*Y_SIZE);
for (unsigned int a=0; a<X_SIZE; ++a){
for (unsigned int b=0; b<Y_SIZE; ++b){
dumpground(&o, wtmp->ground_at(a,b), all_ground, all_item);
}
}
//actors
std::vector<actor *> *all_actor = new std::vector<actor *>();
all_a_table.push_back(all_actor);
all_actor->reserve(X_SIZE*Y_SIZE/3);
for (unsigned int a=0; a<X_SIZE; ++a){
for (unsigned int b=0; b<Y_SIZE; ++b){
dumpactor(&o, wtmp->actor_at(a,b), all_actor, all_item);
}
}
}
void dumpground(std::ofstream *o, ground *gtmp, std::vector<ground *> *table, std::vector<item *> *i_table)
{
unsigned int x=0;
for (x=0; x<table->size(); ++x){
if (gtmp==(*table)[x]){ return; }
}
table->push_back(gtmp);
(*o)<<LS_MARK<<x<<LS_MARK;
(*o)<<gtmp->type()<<LS_SEP<<((gtmp->visible)?"1":"0")<<LS_SEP;
std::vector<item *>::iterator itmp=gtmp->stuff.begin();
while (itmp != gtmp->stuff.end()){
dumpitem(o, (*itmp), i_table);
itmp++;
}
(*o)<<LS_NUL; //cue the end of this ground's items
if (gtmp->conn_world != NULL){
(*o)<<"l."+player->name+"."+s(gtmp->conn_world->level_number)+"."+s(gtmp->conn_world->id)+".lvl"<<LS_SEP;
dumplevel(gtmp->conn_world); //unless dumplevel is correct, this will cause infinite loop
} else {
(*o)<<LS_NUL<<LS_SEP;
}
}
void dumpitem(std::ofstream *o, item *itmp, std::vector<item *> *table)
{
//get correct index to dump. the item should NOT have been saved before, but check if it has.
//if it has already been saved, bail. If not, add it to the collection and save it under the correct mark
unsigned int x=0;
for (x=0; x<table->size(); ++x){
if (itmp==(*table)[x]){ return; } //this may take some time, but it prevents really bad screwups that somehow might occur otherwise.
}
//now the index is correct for what it's position _will_ be...
table->push_back(itmp); //...now.
(*o)<<LS_MARK<<x<<LS_MARK; //and store the item's index. Now actually save the data
//items _should_ be restorable based on name and bonus/subs, and visibility
(*o)<<itmp->data.name<<LS_SEP<<itmp->data.bonus<<LS_SEP;
(*o)<<id_of_sub(itmp->data.sub_equip)<<LS_SEP<<id_of_sub(itmp->data.sub_attack)<<LS_SEP<<id_of_sub(itmp->data.sub_use);
(*o)<<LS_SEP<<((itmp->visible)?"1":"0")<<LS_SEP;
}
void dumpactor(std::ofstream *o, actor *atmp, std::vector<actor *> *table, std::vector<item *> *i_table)
{ //can ignore fragility because, when saving, every monster is unfragile - player's turn has started. So it's okay
if (atmp==NULL){ (*o)<<LS_NUL; return; }
if (is_player(atmp)){ dumpplayer(o, i_table); return; }//player doesn't show up in world normally, but still exists.
unsigned int x=0;
for (x=0; x<table->size(); ++x){
if (atmp==(*table)[x]){ return; }
}
table->push_back(atmp);
(*o)<<LS_MARK<<x<<LS_MARK;
//save every field of m_stat except face_tile because the configuration file could change. (color doesn't matter)
//face_tile should be restorable from name, except in the case of modified monsters (most zombies).
//so when loading, search name for "zombie" and if it exists, force-replace the face tile. Otherwise, just create a new
//monster based on name, then overwrite all data.
(*o)<<atmp->data.name<<LS_SEP<<atmp->data.plural<<LS_SEP<<atmp->data.speed<<LS_SEP<<atmp->data.lvl<<LS_SEP<<atmp->data.str;
(*o)<<LS_SEP<<atmp->data.dex<<LS_SEP<<atmp->data.con<<LS_SEP<<atmp->data.itl<<LS_SEP<<atmp->data.wis<<LS_SEP<<atmp->data.cha;
(*o)<<LS_SEP<<atmp->data.nat_atk<<LS_SEP<<atmp->data.nat_def<<LS_SEP<<atmp->data.dmg_bas<<LS_SEP<<atmp->data.dmg_var;
(*o)<<LS_SEP<<atmp->data.color<<LS_SEP<<atmp->data.status<<LS_SEP<<atmp->max_health<<LS_SEP<<atmp->current_health;
(*o)<<LS_SEP<<atmp->time_offset<<LS_SEP;
//make sure to forceset their x-y coords when loading, because otherwise they will imagine they are at position 0,0
//now inventory. Losing what it was wearing/wielding should be okay, because it should try and wear it again as soon as it moves
std::vector<item *>::iterator itmp=atmp->inventory.begin();
while (itmp != atmp->inventory.end()){
if ((*itmp)->data.name != blank_item.name){
dumpitem(o, (*itmp), i_table);
}
itmp++;
}
(*o)<<LS_NUL;
//end of inventory, now dump a small selection that shows what actor is wielding/carrying
(*o)<<index_of(atmp->wielded, i_table)<<LS_SEP<<index_of(atmp->offwielded, i_table)<<LS_SEP;
(*o)<<index_of(atmp->a_helm, i_table)<<LS_SEP<<index_of(atmp->a_mail, i_table)<<LS_SEP;
(*o)<<index_of(atmp->a_boots, i_table)<<LS_SEP<<index_of(atmp->a_bracers, i_table)<<LS_SEP;
(*o)<<index_of(atmp->a_greaves, i_table)<<LS_SEP<<index_of(atmp->a_fullplate, i_table)<<LS_SEP;
}
void dumpplayer(std::ofstream *o, std::vector<item *> *i_table)
{
//this is designed to go right in with dumpactor. The player will be distinguished from normal actors
//by a LS_MARK of -1, corresponding to no real index. Therefore, this can dump a bit more information than
//normal actors can, including two sets of stats.
(*o)<<LS_MARK<<LS_PLAYER_INDEX<<LS_MARK;
(*o)<<LS_SEP<<player->data.speed<<LS_SEP<<player->data.lvl<<LS_SEP<<player->data.str;
(*o)<<LS_SEP<<player->data.dex<<LS_SEP<<player->data.con<<LS_SEP<<player->data.itl<<LS_SEP<<player->data.wis<<LS_SEP<<player->data.cha;
(*o)<<LS_SEP<<player->data.nat_atk<<LS_SEP<<player->data.nat_def<<LS_SEP<<player->data.dmg_bas<<LS_SEP<<player->data.dmg_var<<LS_SEP;
(*o)<<player->data.color<<LS_SEP<<player->data.status<<LS_SEP<<player->max_health<<LS_SEP<<player->current_health<<LS_SEP;
(*o)<<player->time_offset<<LS_SEP;
//and base data
(*o)<<LS_SEP<<player->base_data.speed<<LS_SEP<<player->base_data.lvl<<LS_SEP<<player->base_data.str;
(*o)<<LS_SEP<<player->base_data.dex<<LS_SEP<<player->base_data.con<<LS_SEP<<player->base_data.itl<<LS_SEP<<player->base_data.wis<<LS_SEP;
(*o)<<player->base_data.cha<<LS_SEP<<player->base_data.nat_atk<<LS_SEP<<player->base_data.nat_def<<LS_SEP<<player->base_data.dmg_bas;
(*o)<<LS_SEP<<player->base_data.dmg_var<<LS_SEP<<player->base_data.color<<LS_SEP<<player->base_data.status<<LS_SEP;
//and inventory
for (int x=0; x<52; ++x){
if (player->inventory[x]!=NULL){
dumpitem(o, player->inventory[x], i_table);
} else {
(*o)<<LS_NUL<<LS_SEP;
}
}
//and wielded/carried
(*o)<<index_of(player->wielded, i_table)<<LS_SEP<<index_of(player->offwielded, i_table)<<LS_SEP;
(*o)<<index_of(player->a_helm, i_table)<<LS_SEP<<index_of(player->a_mail, i_table)<<LS_SEP;
(*o)<<index_of(player->a_boots, i_table)<<LS_SEP<<index_of(player->a_bracers, i_table)<<LS_SEP;
(*o)<<index_of(player->a_greaves, i_table)<<LS_SEP<<index_of(player->a_fullplate, i_table)<<LS_SEP;
}
void dumpevent(std::ofstream *o, event *etmp)
{
//events are universal, not tied to levels. So we need level identifiers on events to tell them which table to look in
//when restoring pointers. so.
//dump format is:
//[world_targets_found_on];my_type;timer;actor_target_id;ground_target_id;item_target_id;int_target_1;int_target_2;[id of function]
unsigned int world=0;
for (world=0; world<all_w_table.size(); ++world){
if ((etmp->my_type==E_NOARGS) ||
(index_of(etmp->actor_target, all_a_table[world])!=LS_NUL_INDEX) ||
(index_of(etmp->item_target, all_i_table[world])!=LS_NUL_INDEX) ||
(index_of(etmp->ground_target, all_g_table[world])!=LS_NUL_INDEX)){
break; //this is the major timesink. ah, well.
}
}
//now world points to the index of where (if anywhere) the event's targets are located
(*o)<<"l."+player->name+"."+s(all_w_table[world]->level_number)+"."+s(all_w_table[world]->id)+".lvl"<<LS_SEP; //world_targets_found_on
(*o)<<etmp->my_type<<LS_SEP;
(*o)<<etmp->timer<<LS_SEP;
(*o)<<(index_of(etmp->actor_target, all_a_table[world]))<<LS_SEP;
(*o)<<(index_of(etmp->ground_target, all_g_table[world]))<<LS_SEP;
(*o)<<(index_of(etmp->item_target, all_i_table[world]))<<LS_SEP;
(*o)<<etmp->int_target_1<<LS_SEP<<etmp->int_target_2<<LS_SEP;
if (etmp->my_type==E_NOARGS){
(*o)<<id_of_function(etmp->f_no_args);
} else if (etmp->my_type==E_ITEM_TARGET){
(*o)<<id_of_function(etmp->f_item_target);
} else if (etmp->my_type==E_ACTOR_TARGET){
(*o)<<id_of_function(etmp->f_actor_target);
} else if (etmp->my_type==E_GROUND_TARGET){
(*o)<<id_of_function(etmp->f_ground_target);
} else if (etmp->my_type==E_INT_INT_ITEM_TARGET){
(*o)<<id_of_function(etmp->f_ground_item_target);
} else if (etmp->my_type==E_INT_INT_ACTOR_ITEM_GROUND_TARGET){
(*o)<<id_of_function(etmp->f_int_int_actor_item_ground_target);
}
(*o)<<LS_SEP;
}
/***************************************************************/
/***************************************************************/
//now comes level LOADING
/***************************************************************/
/***************************************************************/
worldlet *restorelevel(std::string filename)
{
#ifdef DEBUG
tell(("restoring "+filename).c_str());
#endif
/*
* this is a more hideously monolithic function than usual, because dumping items
* only requires passing pointers, while restoring items involves creating them.
* there might be issues with that, which this eliminates by sacrificing elegance.
*
*/
std::filebuf fb;
fb.open(filename.c_str(), std::ios::in);
std::istream i(&fb);
std::vector<worldlet *>::iterator wtmp=l_all_w_table.begin();
//std::cout<<"::::::::::::";
while (wtmp!=l_all_w_table.end()){
std::string tmp="l."+player->name+"."+s((*wtmp)->level_number)+"."+s((*wtmp)->id)+".lvl";
if (filename==tmp){ return &(**wtmp); }
//check all other levels that are/might be being restored. if the strings match, kill and get out.
++wtmp;
}
worldlet *new_w = new worldlet();
std::vector<ground *> *l_g_table=new std::vector<ground *>();
std::vector<item *> *l_i_table=new std::vector<item *>();
std::vector<actor *> *l_a_table=new std::vector<actor *>();
//now a skeleton level needs to be constructed such that it will fill the above criteria.
//specifically, it needs level_number and id. those can be extracted from the filename.
//then the file is opened and restoration starts by reading in the ground.
//of course, it would be easy to just treat the entire file as a stream and parse bits of it into
//correct places - completely cutting out the getline dances. if you're reading this, then it was
//written still in the debugging stage and someone forgot to go back and do that.
std::string s_version, s_level_number, s_id, s_x_size, s_y_size, s_lighting, s_dump;
getline(i,s_version,LS_SEP);
getline(i,s_level_number,LS_SEP);
getline(i,s_id,LS_SEP);
getline(i,s_lighting,LS_SEP);
getline(i,s_dump,LS_SEP); //x_size
getline(i,s_dump,LS_SEP); //y_size - not used here
new_w->level_number=ival(s_level_number.c_str());
new_w->id=ival(s_id.c_str());
if (new_w->id >= latest_id){
latest_id=new_w->id+2;
}
//ignore X_SIZE/Y_SIZE. they should only be used when splicing levels to determine compatibility.
new_w->lighting=ival(s_lighting);
l_all_w_table.push_back(new_w); //id is already restorable, and index table must be built quickly to prevent infiniloops
l_all_g_table.push_back(l_g_table);
l_all_i_table.push_back(l_i_table);
l_all_a_table.push_back(l_a_table);
//restore ground.
std::string g_index, g_type, g_visible, g_conn;
int ig_index=0, ig_type;
std::string i_index, i_name, i_bonus, i_csub, i_vis;
int ii_index;
l_g_table->reserve((X_SIZE*Y_SIZE)+1);
unsigned int cx;
unsigned int cy;
for (cx=0; cx<X_SIZE; cx++){
for (cy=0; cy<Y_SIZE; cy++){
#ifdef DEBUG
tell(("restoring ground at "+s(cx)+","+s(cy)).c_str());
#endif
getline(i,s_dump,LS_MARK);//should return NULL. check here for errors
getline(i,g_index,LS_MARK);
ig_index=ival(g_index); //local index - where the ground should be created
getline(i,g_type,LS_SEP);
ig_type=ival(g_type); //type of ground. Now the skeleton can be made where it needs to go
(*l_g_table)[ig_index] = new ground(static_cast<ground_type>(ig_type));
new_w->floorplan[cy][cx]=(*l_g_table)[ig_index];
getline(i,g_visible,LS_SEP);
if (g_visible.c_str()[0]=='1'){
(*l_g_table)[ig_index]->visible=true; //true==1
} else {
(*l_g_table)[ig_index]->visible=false; //false==0
}
//now start reading in items until LS_NUL is hit
while (i.peek()!=LS_NUL){ //next character MUST be a LS_MARK, indicating beginning of an item block
getline(i,s_dump,LS_MARK);//first mark begins, so will be NUL stored.
getline(i,i_index,LS_MARK);
//read to next mark, store as ii_index
ii_index=ival(i_index);
getline(i,i_name,LS_SEP);
std::vector<item *>::iterator it=l_i_table->begin();
for (int intmp=0; intmp<ii_index; ++intmp){
if(it==l_i_table->end()){
l_i_table->push_back(NULL);
it=l_i_table->end();
} else {
++it;
}
}
if (ii_index==0){
l_i_table->push_back(new item(i_name));
} else {
l_i_table->insert(it, new item(i_name));
}
/*
l_i_table->reserve(ii_index+1);
(*l_i_table)[ii_index] = new item(i_name);
*/
getline(i,i_bonus,LS_SEP);
(*l_i_table)[ii_index]->data.bonus=ival(i_bonus);
//get onequip sub
getline(i,i_csub,LS_SEP);
(*l_i_table)[ii_index]->data.sub_equip=equip_sub_of_id(ival(i_csub));
//get onattack sub
getline(i,i_csub,LS_SEP);
(*l_i_table)[ii_index]->data.sub_attack=attack_sub_of_id(ival(i_csub));
//get onuse sub
getline(i,i_csub,LS_SEP);
(*l_i_table)[ii_index]->data.sub_use=use_sub_of_id(ival(i_csub));
//read and store visible
getline(i,i_vis,LS_SEP);
if (i_vis.c_str()[0]=='1'){
(*l_i_table)[ii_index]->visible=true;
} else {
(*l_i_table)[ii_index]->visible=false;
}
(*l_g_table)[ig_index]->drop_upon((*l_i_table)[ii_index]);
}//at this point, all items have been read in, and next char is an LS_NUL
getline(i,s_dump,LS_NUL); //skip the nul. now either [<new world>LS_SEP] or [LS_NUL LS_SEP].
if (i.peek()!=LS_NUL){ //must be a new level!
getline(i,g_conn,LS_SEP);
(*l_g_table)[ig_index]->conn_world=restorelevel(g_conn);
} else {
getline(i,s_dump,LS_SEP); //it's a null anyway.
}
}
}
//now all ground for this level has been read in. Time to bring in the actors.
std::string a_index, a_stmp;
int ia_index;
actor *atmp=NULL;
for (cx=0; cx<X_SIZE; ++cx){
for (cy=0; cy<Y_SIZE; ++cy){
if (i.peek()==LS_NUL){
getline(i,s_dump,LS_NUL);
} else {
getline(i,s_dump,LS_MARK);
getline(i,a_index,LS_MARK);
if (a_index!=s(LS_PLAYER_INDEX)){//normal monster
ia_index=ival(a_index);
getline(i,a_stmp,LS_SEP);//name
#ifdef DEBUG
tell(("restoring a "+a_stmp).c_str());
#endif
std::vector<actor *>::iterator it=l_a_table->begin();
for (int intmp=0; intmp<ia_index; ++intmp){
if(it==l_a_table->end()){
l_a_table->push_back(NULL);
++it;
} else {
++it;
}
}
if (ia_index==0){
l_a_table->push_back(new monster(a_stmp));
} else{
l_a_table->insert(it, new monster(a_stmp));
}
atmp=(*l_a_table)[ia_index];
if (atmp->data.face_tile==F_BLANK){ //zombies are sometimes dynamically generated
atmp->data.face_tile=F_ZOMBIE; //see dumpactor
}
new_w->ins_actr(cx, cy, (*l_a_table)[ia_index]);
GET_ACTOR_VAR(data.plural);
GET_ACTOR_VAR(data.speed);
GET_ACTOR_VAR(data.lvl);
GET_ACTOR_VAR(data.str);
GET_ACTOR_VAR(data.dex);
GET_ACTOR_VAR(data.con);
GET_ACTOR_VAR(data.itl);
GET_ACTOR_VAR(data.wis);
GET_ACTOR_VAR(data.cha);
GET_ACTOR_VAR(data.nat_atk);
GET_ACTOR_VAR(data.nat_def);
GET_ACTOR_VAR(data.dmg_bas);
GET_ACTOR_VAR(data.dmg_var);
GET_ACTOR_VAR(data.color);
getline(i,a_stmp,LS_SEP); //todo: rewrite this so that it takes in only one character
atmp->data.status=a_stmp.c_str()[0];
GET_ACTOR_VAR(max_health);
GET_ACTOR_VAR(current_health);
GET_ACTOR_VAR(time_offset);
atmp->fragile=false;
//now inventory... same as ground's
#ifdef DEBUG
tell(("restoring items of "+a_stmp).c_str());
#endif
while (i.peek()==LS_MARK){ //next character MUST be a LS_MARK, indicating beginning of an item block
getline(i,s_dump,LS_MARK);//first mark begins, so will be NUL stored.
getline(i,i_index,LS_MARK);
//read to next mark, store as ii_index
ii_index=ival(i_index);
getline(i,i_name,LS_SEP);
std::vector<item *>::iterator it=l_i_table->begin();
for (int intmp=0; intmp<ii_index; ++intmp){
if(it==l_i_table->end()){
l_i_table->push_back(NULL);
++it;
} else {
++it;
}
}
if (ii_index==0){
l_i_table->push_back(new item(i_name));
} else {
l_i_table->insert(it, new item(i_name));
}
getline(i,i_bonus,LS_SEP);
(*l_i_table)[ii_index]->data.bonus=ival(i_bonus);
//get onequip sub
getline(i,i_csub,LS_SEP);
(*l_i_table)[ii_index]->data.sub_equip=equip_sub_of_id(ival(i_csub));
//get onattack sub
getline(i,i_csub,LS_SEP);
(*l_i_table)[ii_index]->data.sub_attack=attack_sub_of_id(ival(i_csub));
//get onuse sub
getline(i,i_csub,LS_SEP);
(*l_i_table)[ii_index]->data.sub_use=use_sub_of_id(ival(i_csub));
//read and store visible
getline(i,i_vis,LS_SEP);
if (i_vis.c_str()[0]=='1'){
(*l_i_table)[ii_index]->visible=true;
} else {
(*l_i_table)[ii_index]->visible=false;
}
}//at this point, all items have been read in, and next char is an LS_NUL
#ifdef DEBUG
tell(("read in items of "+a_stmp).c_str());
#endif
getline(i,s_dump,LS_NUL); //skip the nul.
//now comes what it's carrying.
GET_ACTOR_IPOINTER(wielded);
GET_ACTOR_IPOINTER(offwielded);
GET_ACTOR_IPOINTER(a_helm);
GET_ACTOR_IPOINTER(a_mail);
GET_ACTOR_IPOINTER(a_boots);
GET_ACTOR_IPOINTER(a_bracers);
GET_ACTOR_IPOINTER(a_greaves);
GET_ACTOR_IPOINTER(a_fullplate);
new_w->ins_actr(cx, cy, (*l_a_table)[ia_index]); //for some reason, doesn't work as intended. why?
} else { //player is dumped differently, 2 stat blocks &c.
getline(i,s_dump,LS_SEP);
current_world=new_w;
current_world->ins_actr(cx, cy, player);
a_index="0";//if a_index is untouched, some sort of bug currently exists that elevates issues with actor restoration
GET_PLAYER_VAR(data.speed);
GET_PLAYER_VAR(data.lvl);
GET_PLAYER_VAR(data.str);
GET_PLAYER_VAR(data.dex);
GET_PLAYER_VAR(data.con);
GET_PLAYER_VAR(data.itl);
GET_PLAYER_VAR(data.wis);
GET_PLAYER_VAR(data.cha);
GET_PLAYER_VAR(data.nat_atk);
GET_PLAYER_VAR(data.nat_def);
GET_PLAYER_VAR(data.dmg_bas);
GET_PLAYER_VAR(data.dmg_var);
GET_PLAYER_VAR(data.color);
getline(i,a_stmp,LS_SEP); //todo: rewrite this so that it takes in only one character
player->data.status=a_stmp.c_str()[0];
GET_PLAYER_VAR(max_health);
GET_PLAYER_VAR(current_health);
GET_PLAYER_VAR(time_offset);
//-----------------------------------//
getline(i,s_dump,LS_SEP);
GET_PLAYER_VAR(base_data.speed);
GET_PLAYER_VAR(base_data.lvl);
GET_PLAYER_VAR(base_data.str);
GET_PLAYER_VAR(base_data.dex);
GET_PLAYER_VAR(base_data.con);
GET_PLAYER_VAR(base_data.itl);
GET_PLAYER_VAR(base_data.wis);
GET_PLAYER_VAR(base_data.cha);
GET_PLAYER_VAR(base_data.nat_atk);
GET_PLAYER_VAR(base_data.nat_def);
GET_PLAYER_VAR(base_data.dmg_bas);
GET_PLAYER_VAR(base_data.dmg_var);
GET_PLAYER_VAR(base_data.color);
getline(i,a_stmp,LS_SEP); //todo: rewrite this so that it takes in only one character
player->base_data.status=a_stmp.c_str()[0];
for (int x=0; x<52; ++x){
if (i.peek()!=LS_NUL){
getline(i,s_dump,LS_MARK);//first mark begins, so will be NUL stored.
getline(i,i_index,LS_MARK);
//read to next mark, store as ii_index
ii_index=ival(i_index);
getline(i,i_name,LS_SEP);
std::vector<item *>::iterator it=l_i_table->begin();
for (int intmp=0; intmp<ii_index; ++intmp){
if(it==l_i_table->end()){
l_i_table->push_back(NULL);
++it;
} else {
++it;
}
}
l_i_table->insert(it, new item(i_name));
getline(i,i_bonus,LS_SEP);
(*l_i_table)[ii_index]->data.bonus=ival(i_bonus);
//get onequip sub
getline(i,i_csub,LS_SEP);
(*l_i_table)[ii_index]->data.sub_equip=equip_sub_of_id(ival(i_csub));
//get onattack sub
getline(i,i_csub,LS_SEP);
(*l_i_table)[ii_index]->data.sub_attack=attack_sub_of_id(ival(i_csub));
//get onuse sub
getline(i,i_csub,LS_SEP);
(*l_i_table)[ii_index]->data.sub_use=use_sub_of_id(ival(i_csub));
//read and store visible
getline(i,i_vis,LS_SEP);
//hack alert!
if (i_vis.c_str()[0]=='1'){
(*l_i_table)[ii_index]->visible=true; //true=1
} else {
(*l_i_table)[ii_index]->visible=false; //and false=0
}
player->inventory[x]=((*l_i_table)[ii_index]);
} else {
getline(i,s_dump,LS_SEP);//player always dumps nul to maintain inv order
}
}
GET_PLAYER_IPOINTER(wielded);
GET_PLAYER_IPOINTER(offwielded);
GET_PLAYER_IPOINTER(a_helm);
GET_PLAYER_IPOINTER(a_mail);
GET_PLAYER_IPOINTER(a_boots);
GET_PLAYER_IPOINTER(a_bracers);
GET_PLAYER_IPOINTER(a_greaves);
GET_PLAYER_IPOINTER(a_fullplate);
}
}
}
}
#ifdef DEBUG
tell("world should be restored now");
#endif
return new_w;
}
bool restoregame() //this should be complete name of file, but relative pathing is allowed
{
struct stat fileInfo;
std::string filename = player->name+".sav"; //todo: point this to /var/games/tzar/save or something...
if (stat(filename.c_str(), &fileInfo)!=0){ //savefile doesn't exists...
return false;
}
/////////////////
tell("Restoring save file...");
/////////////////
if (LS_MARK==LS_NUL || LS_MARK==LS_SEP || LS_SEP==LS_NUL){
tell("This executable has been compiled in a way that precludes level loading. To correct this, make sure the values LS_MARK, LS_SEP, and LS_NUL in saverestore.h are UNIQUE, then recompile.");
return false;
}
std::string s_dump;
std::filebuf fb;
fb.open(filename.c_str(), std::ios::in);
std::istream i(&fb);
l_all_i_table.clear();
l_all_g_table.clear();
l_all_a_table.clear();
l_all_w_table.clear();
#ifdef DEBUG
tell("now checking versioning");
#endif
std::string t_version="";
getline(i,t_version,LS_SEP);
bool compatible_file=(t_version==VERSION);
for (unsigned int qq=0; (qq<(sizeof( compat_versions)/sizeof( compat_versions[0])) && !compatible_file); ++qq){
if (t_version==compat_versions[qq]){ compatible_file=true; }
}
if (!compatible_file){
std::string a="Current version: "+VERSION+". Load save file from version "+t_version+"?";
if (!(confirm(a.c_str()))){ register_msg("Fine."); return false; }
}
//first, restore the player's name. A few other algorithms try and recreate save names for
//redundancy checking, so the player's name needs to be extracted
std::string start_world="";
getline(i,start_world,LS_SEP);
restorelevel(start_world);
std::string s_timer;
getline(i,s_timer,LS_SEP);
action_timer=ival(s_timer);
getline(i,s_dump,LS_SEP);//player x
getline(i,s_dump,LS_SEP);//player y
//now come events/
#ifdef DEBUG
tell("reading in events now.");
#endif
std::string right_world, test_world, e_stmp;
event *e_tmp = new event;
int w_index=0;
std::vector<worldlet *>::iterator wtmp=l_all_w_table.begin();
while (i.peek()!=LS_NUL){
getline(i,right_world,LS_SEP); //should be impossible for a LS_SEP to be _in_ world name
while (wtmp!=l_all_w_table.end()){
test_world="l."+player->name+"."+s((*wtmp)->level_number)+"."+s((*wtmp)->id)+".lvl";
if (right_world==test_world){
wtmp=l_all_w_table.end();
} else {
wtmp++;
w_index++;
}
}//now w_index has the correct index for all top-level vectors. lower level indicies will now be read in.
#ifdef DEBUG
tell(("got right world for an event, id-->"+right_world).c_str());
#endif
getline (i,e_stmp,LS_SEP); //type
e_tmp->my_type=static_cast<event_type>(ival(e_stmp));
getline (i,e_stmp,LS_SEP); //timer
e_tmp->timer=ival(e_stmp);
getline(i,e_stmp,LS_SEP); //actor target
if (e_tmp->my_type==E_ACTOR_TARGET){
e_tmp->actor_target=((ival(e_stmp)==LS_NUL_INDEX)?NULL:l_all_a_table[w_index]->at(ival(e_stmp)));
}
getline(i,e_stmp,LS_SEP); //ground target
if (e_tmp->my_type==E_GROUND_TARGET){
e_tmp->ground_target=((ival(e_stmp)==LS_NUL_INDEX)?NULL:l_all_g_table[w_index]->at(ival(e_stmp)));
}
getline(i,e_stmp,LS_SEP); //item target
if (e_tmp->my_type==E_ITEM_TARGET || e_tmp->my_type==E_INT_INT_ITEM_TARGET){
e_tmp->item_target=((ival(e_stmp)==LS_NUL_INDEX)?NULL:l_all_i_table[w_index]->at(ival(e_stmp)));
}
getline(i,e_stmp,LS_SEP); //int1 target
e_tmp->int_target_1=ival(e_stmp);
getline(i,e_stmp,LS_SEP); //int2 target
e_tmp->int_target_2=ival(e_stmp);
getline(i,e_stmp,LS_SEP); //function target
//of course, etmp can't get pushed back because it will get recreated next run-through. this is the next-best thing.
#ifdef DEBUG
tell("got an event, adding it now. timer->");
#endif
if (ival(e_stmp)>=0){
if (e_tmp->my_type==E_NOARGS){
new_happenings.push_back(mk_event(e_tmp->timer, f_voids[ival(e_stmp)]));
} else if (e_tmp->my_type==E_ITEM_TARGET){
new_happenings.push_back(mk_event(e_tmp->timer, f_items[ival(e_stmp)], e_tmp->item_target));
} else if (e_tmp->my_type==E_ACTOR_TARGET){
new_happenings.push_back(mk_event(e_tmp->timer, f_actors[ival(e_stmp)], e_tmp->actor_target));
} else if (e_tmp->my_type==E_GROUND_TARGET){
new_happenings.push_back(mk_event(e_tmp->timer, f_grounds[ival(e_stmp)], e_tmp->ground_target));
} else if (e_tmp->my_type==E_INT_INT_ITEM_TARGET){
new_happenings.push_back(mk_event(e_tmp->timer, f_int_int_items[ival(e_stmp)], e_tmp->int_target_1, e_tmp->int_target_2, e_tmp->item_target));
} else if (e_tmp->my_type==E_INT_INT_ACTOR_ITEM_GROUND_TARGET){
new_happenings.push_back(mk_event(e_tmp->timer, f_int_int_actor_item_grounds[ival(e_stmp)], e_tmp->int_target_1, e_tmp->int_target_2, e_tmp->actor_target, e_tmp->item_target, e_tmp->ground_target));
}
}
}
#ifdef DEBUG
tell("restore successful.");
#endif
return true;
}