Add tests for reading player data from a saved game
- Changed cPlayer::equip to a bitset - Use a static constant instead of a loop to initialized player starting spells - Only save spell points if the player has any (current if different from max) - Symbolic forms for trait enum (and save symbolic forms also for skills) - When loading a player, clear data which is not always present in the file - Also add an init test for cPlayer
This commit is contained in:
@@ -253,6 +253,8 @@
|
||||
91BC33921B4388E80008882C /* libboost_thread.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 910D9CA31B36439100414B17 /* libboost_thread.dylib */; };
|
||||
91BC33981B4481EF0008882C /* scen.fileio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91B3EEF20F969BA700BF5B67 /* scen.fileio.cpp */; };
|
||||
91BFA3D71901B18F001686E4 /* mask.vert in Copy Shaders */ = {isa = PBXBuildFile; fileRef = 91BFA3D61901B024001686E4 /* mask.vert */; };
|
||||
91C548F81D8B2FE400FE6A7B /* pc_read.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91C548F71D8B2EE400FE6A7B /* pc_read.cpp */; };
|
||||
91C548F91D8B338700FE6A7B /* pc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 913D05BB0FA1EA0A00184C18 /* pc.cpp */; };
|
||||
91C6864A0FD5EEFD000F6D01 /* pc.graphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91B3EF0A0F969BD300BF5B67 /* pc.graphics.cpp */; };
|
||||
91C749BA1A2D670D008E0E10 /* dialogs in Copy Data Files */ = {isa = PBXBuildFile; fileRef = 91C749B91A2D66F7008E0E10 /* dialogs */; };
|
||||
91C763D91B4C50710086D879 /* enums.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91C763D81B4C4BB30086D879 /* enums.cpp */; };
|
||||
@@ -738,6 +740,7 @@
|
||||
91C2A6EC1B8FA91400346948 /* town_read.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = town_read.cpp; sourceTree = "<group>"; };
|
||||
91C2A6ED1B8FA9FB00346948 /* out_read.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = out_read.cpp; sourceTree = "<group>"; };
|
||||
91C2A6EE1B8FAA8E00346948 /* talk_read.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = talk_read.cpp; sourceTree = "<group>"; };
|
||||
91C548F71D8B2EE400FE6A7B /* pc_read.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pc_read.cpp; sourceTree = "<group>"; };
|
||||
91C688E60FD702B9000F6D01 /* cursors.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cursors.hpp; sourceTree = "<group>"; };
|
||||
91C688E70FD702B9000F6D01 /* cursors.mac.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = cursors.mac.mm; sourceTree = "<group>"; };
|
||||
91C749B71A2D6432008E0E10 /* strings */ = {isa = PBXFileReference; lastKnownFileType = folder; path = strings; sourceTree = "<group>"; };
|
||||
@@ -1360,6 +1363,7 @@
|
||||
9176FEC01D550EFC006EF694 /* out_legacy.cpp */,
|
||||
91C2A6ED1B8FA9FB00346948 /* out_read.cpp */,
|
||||
91E381491B97678D00F69B81 /* out_write.cpp */,
|
||||
91C548F71D8B2EE400FE6A7B /* pc_read.cpp */,
|
||||
9176FEC11D550EFC006EF694 /* scen_legacy.cpp */,
|
||||
91CC173A1B421CA0003D9A69 /* scen_read.cpp */,
|
||||
91CC173B1B421CA0003D9A69 /* scen_write.cpp */,
|
||||
@@ -1904,6 +1908,8 @@
|
||||
9176FEC81D550EFE006EF694 /* scen_legacy.cpp in Sources */,
|
||||
9176FECB1D550EFE006EF694 /* talk_legacy.cpp in Sources */,
|
||||
9176FECC1D550EFE006EF694 /* town_legacy.cpp in Sources */,
|
||||
91C548F81D8B2FE400FE6A7B /* pc_read.cpp in Sources */,
|
||||
91C548F91D8B338700FE6A7B /* pc.cpp in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
@@ -166,6 +166,24 @@ std::istream& operator >> (std::istream& in, eSkill& e){
|
||||
return in;
|
||||
}
|
||||
|
||||
// MARK: eTrait
|
||||
|
||||
cEnumLookup trait_names = {
|
||||
"tough", "magic-apt", "ambidex", "nimble", "cave-lore", "wood-lore", "const", "alert",
|
||||
"strong", "regen", "slow", "magic-inept", "frail", "sickly", "bad-back", "pacifist", "anama"
|
||||
};
|
||||
|
||||
std::ostream& operator << (std::ostream& out, eTrait e) {
|
||||
writeEnum(out, e, trait_names, "tough");
|
||||
return out;
|
||||
}
|
||||
|
||||
std::istream& operator >> (std::istream& in, eTrait& e){
|
||||
if(!readEnum(in, e, trait_names, eTrait::TOUGHNESS))
|
||||
in.setstate(std::ios::failbit);
|
||||
return in;
|
||||
}
|
||||
|
||||
// MARK: eItemType
|
||||
|
||||
cEnumLookup item_types = {
|
||||
|
||||
@@ -23,6 +23,8 @@ extern const std::multiset<eItemType> num_hands_to_use;
|
||||
extern std::map<const eItemType, const short> excluding_types;
|
||||
|
||||
extern short skill_bonus[21];
|
||||
// A nice convenient bitset with just the low 30 bits set, for initializing spells
|
||||
const uint32_t cPlayer::basic_spells = std::numeric_limits<uint32_t>::max() >> 2;
|
||||
|
||||
void cPlayer::import_legacy(legacy::pc_record_type old){
|
||||
main_status = (eMainStatus) old.main_status;
|
||||
@@ -940,12 +942,10 @@ cPlayer::cPlayer(cParty& party) : party(&party), weap_poisoned(*this) {
|
||||
skill_pts = 65;
|
||||
level = 1;
|
||||
std::fill(items.begin(), items.end(), cItem());
|
||||
std::fill(equip.begin(), equip.end(), false);
|
||||
equip.reset();
|
||||
|
||||
for(short i = 0; i < 62; i++) {
|
||||
priest_spells[i] = i < 30;
|
||||
mage_spells[i] = i < 30;
|
||||
}
|
||||
priest_spells = basic_spells;
|
||||
mage_spells = basic_spells;
|
||||
which_graphic = 0;
|
||||
|
||||
race = eRace::HUMAN;
|
||||
@@ -995,7 +995,7 @@ cPlayer::cPlayer(cParty& party,long key,short slot) : cPlayer(party) {
|
||||
skill_pts = 60;
|
||||
level = 1;
|
||||
std::fill(items.begin(), items.end(), cItem());
|
||||
std::fill(equip.begin(), equip.end(), false);
|
||||
equip.reset();
|
||||
|
||||
priest_spells.set();
|
||||
mage_spells.set();
|
||||
@@ -1084,13 +1084,9 @@ cPlayer::cPlayer(cParty& party,long key,short slot) : cPlayer(party) {
|
||||
level = 1;
|
||||
|
||||
std::fill(items.begin(), items.end(), cItem());
|
||||
std::fill(equip.begin(), equip.end(), false);
|
||||
equip.reset();
|
||||
cur_sp = pc_sp[slot];
|
||||
max_sp = pc_sp[slot];
|
||||
for(short i = 0; i < 62; i++) {
|
||||
priest_spells[i] = i < 30;
|
||||
mage_spells[i] = i < 30;
|
||||
}
|
||||
for(short i = 0; i < 15; i++) {
|
||||
eTrait trait = eTrait(i);
|
||||
traits[trait] = pc_t[slot].count(trait);
|
||||
@@ -1180,14 +1176,16 @@ void cPlayer::writeTo(std::ostream& file) const {
|
||||
file << "UID " << unique_id << '\n';
|
||||
file << "STATUS -1 " << main_status << '\n';
|
||||
file << "NAME " << maybe_quote_string(name) << '\n';
|
||||
file << "SKILL 19 " << max_health << '\n';
|
||||
file << "SKILL 20 " << max_sp << '\n';
|
||||
file << "SKILL " << eSkill::MAX_HP << ' ' << max_health << '\n';
|
||||
if(max_sp > 0)
|
||||
file << "SKILL " << eSkill::MAX_SP << ' ' << max_sp << '\n';
|
||||
for(auto p : skills) {
|
||||
if(p.second > 0)
|
||||
file << "SKILL " << int(p.first) << ' ' << p.second << '\n';
|
||||
}
|
||||
file << "HEALTH " << cur_health << '\n';
|
||||
file << "MANA " << cur_sp << '\n';
|
||||
if(cur_sp != max_sp)
|
||||
file << "MANA " << cur_sp << '\n';
|
||||
file << "EXPERIENCE " << experience << '\n';
|
||||
file << "SKILLPTS " << skill_pts << '\n';
|
||||
file << "LEVEL " << level << '\n';
|
||||
@@ -1231,7 +1229,17 @@ void cPlayer::readFrom(std::istream& file){
|
||||
std::string cur;
|
||||
getline(file, cur, '\f');
|
||||
bin.str(cur);
|
||||
std::fill(equip.begin(), equip.end(), false);
|
||||
|
||||
// Clear some data that is not always present
|
||||
equip.reset();
|
||||
mage_spells.reset();
|
||||
priest_spells.reset();
|
||||
weap_poisoned.clear();
|
||||
status.clear();
|
||||
traits.clear();
|
||||
skills.clear();
|
||||
cur_sp = max_sp = ap = 0;
|
||||
|
||||
while(bin) { // continue as long as no error, such as eof, occurs
|
||||
getline(bin, cur);
|
||||
sin.str(cur);
|
||||
@@ -1244,20 +1252,30 @@ void cPlayer::readFrom(std::istream& file){
|
||||
}else if(cur == "NAME")
|
||||
name = read_maybe_quoted_string(sin);
|
||||
else if(cur == "SKILL"){
|
||||
int i;
|
||||
sin >> i;
|
||||
switch(i){
|
||||
case -1:
|
||||
case 20:
|
||||
sin >> max_sp;
|
||||
break;
|
||||
case -2:
|
||||
case 19:
|
||||
eSkill skill;
|
||||
sin >> skill;
|
||||
switch(skill) {
|
||||
case eSkill::MAX_HP:
|
||||
sin >> max_health;
|
||||
break;
|
||||
case eSkill::MAX_SP:
|
||||
sin >> max_sp;
|
||||
break;
|
||||
case eSkill::CUR_HP:
|
||||
sin >> cur_health;
|
||||
break;
|
||||
case eSkill::CUR_SP:
|
||||
sin >> cur_sp;
|
||||
break;
|
||||
case eSkill::CUR_XP:
|
||||
sin >> experience;
|
||||
break;
|
||||
case eSkill::CUR_SKILL:
|
||||
break;
|
||||
case eSkill::CUR_LEVEL:
|
||||
sin >> level;
|
||||
break;
|
||||
default:
|
||||
if(i < 0 || i >= 19) break;
|
||||
eSkill skill = eSkill(i);
|
||||
sin >> skills[skill];
|
||||
}
|
||||
}else if(cur == "HEALTH")
|
||||
@@ -1291,10 +1309,8 @@ void cPlayer::readFrom(std::istream& file){
|
||||
sin >> i;
|
||||
priest_spells[i] = true;
|
||||
}else if(cur == "TRAIT"){
|
||||
int i;
|
||||
sin >> i;
|
||||
if(i < 0 || i > 15) continue;
|
||||
eTrait trait = eTrait(i);
|
||||
eTrait trait;
|
||||
sin >> trait;
|
||||
traits[trait] = true;
|
||||
}else if(cur == "ICON")
|
||||
sin >> which_graphic;
|
||||
|
||||
@@ -64,7 +64,10 @@ class cPlayer : public iLiving {
|
||||
cParty* party;
|
||||
template<typename Fcn>
|
||||
cInvenSlot find_item_matching(Fcn fcn);
|
||||
static const int INVENTORY_SIZE = 24;
|
||||
public:
|
||||
// A nice convenient bitset with just the low 30 bits set, for initializing spells
|
||||
static const uint32_t basic_spells;
|
||||
static void(* give_help)(short,short);
|
||||
eMainStatus main_status;
|
||||
std::string name;
|
||||
@@ -77,8 +80,8 @@ public:
|
||||
unsigned short experience;
|
||||
short skill_pts;
|
||||
short level;
|
||||
std::array<cItem,24> items;
|
||||
std::array<bool,24> equip;
|
||||
std::array<cItem,INVENTORY_SIZE> items;
|
||||
std::bitset<INVENTORY_SIZE> equip;
|
||||
std::bitset<62> priest_spells;
|
||||
std::bitset<62> mage_spells;
|
||||
pic_num_t which_graphic;
|
||||
@@ -90,7 +93,7 @@ public:
|
||||
// transient stuff
|
||||
std::map<eSkill,eSpell> last_cast;
|
||||
location combat_pos;
|
||||
short parry;
|
||||
short parry = 0;
|
||||
iLiving* last_attacked = nullptr; // Note: Currently this is assigned but never read
|
||||
|
||||
bool is_alive() const;
|
||||
@@ -175,5 +178,7 @@ void operator += (eMainStatus& stat, eMainStatus othr);
|
||||
void operator -= (eMainStatus& stat, eMainStatus othr);
|
||||
std::ostream& operator << (std::ostream& out, eMainStatus e);
|
||||
std::istream& operator >> (std::istream& in, eMainStatus& e);
|
||||
std::ostream& operator << (std::ostream& out, eTrait e);
|
||||
std::istream& operator >> (std::istream& in, eTrait& e);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user