Merge mac into windows

This commit is contained in:
2017-08-27 23:37:37 -04:00
228 changed files with 3446 additions and 3423 deletions

655
src/universe/creature.cpp Normal file
View File

@@ -0,0 +1,655 @@
//
// creature.cpp
// BoE
//
// Created by Celtic Minstrel on 15-01-24.
//
//
#include "creature.hpp"
#include <iostream>
#include <sstream>
#include "oldstructs.hpp"
#include "mathutil.hpp"
#include "pc.hpp"
#include "spell.hpp"
const short cCreature::charm_odds[21] = {90,90,85,80,78, 75,73,60,40,30, 20,10,4,1,0, 0,0,0,0,0, 0};
cCreature::cCreature() {
attitude = eAttitude::DOCILE;
cur_loc.x = cur_loc.y = targ_loc.x = targ_loc.y = 80;
}
cCreature::cCreature(int num) : cCreature() {
number = num;
}
void cCreature::import_legacy(legacy::creature_data_type old){
active = old.active;
attitude = eAttitude(old.attitude);
number = old.number;
cur_loc.x = old.m_loc.x;
cur_loc.y = old.m_loc.y;
cMonster::import_legacy(old.m_d);
mobility = old.mobile;
summon_time = old.summoned;
if(summon_time >= 100) {
party_summoned = false;
summon_time -= 100;
} else party_summoned = true;
number = old.monst_start.number;
start_attitude = eAttitude(old.monst_start.start_attitude);
start_loc.x = old.monst_start.start_loc.x;
start_loc.y = old.monst_start.start_loc.y;
mobility = old.monst_start.mobile;
switch(old.monst_start.time_flag) {
case 0: time_flag = eMonstTime::ALWAYS; break;
case 1: time_flag = eMonstTime::APPEAR_ON_DAY; break;
case 2: time_flag = eMonstTime::DISAPPEAR_ON_DAY; break;
case 4: time_flag = eMonstTime::SOMETIMES_A; break;
case 5: time_flag = eMonstTime::SOMETIMES_B; break;
case 6: time_flag = eMonstTime::SOMETIMES_C; break;
case 7: time_flag = eMonstTime::APPEAR_WHEN_EVENT; break;
case 8: time_flag = eMonstTime::DISAPPEAR_WHEN_EVENT; break;
}
spec1 = old.monst_start.spec1;
spec2 = old.monst_start.spec2;
spec_enc_code = old.monst_start.spec_enc_code;
time_code = old.monst_start.time_code;
monster_time = old.monst_start.monster_time;
personality = old.monst_start.personality;
special_on_kill = old.monst_start.special_on_kill;
facial_pic = old.monst_start.facial_pic;
health = old.m_d.health;
mp = old.m_d.mp;
max_mp = old.m_d.max_mp;
ap = old.m_d.ap;
morale = old.m_d.morale;
m_morale = old.m_d.m_morale;
for(int i = 0; i < 15; i++)
status[(eStatus) i] = old.m_d.status[i];
direction = eDirection(old.m_d.direction);
}
void cCreature::avatar() {
health = m_health;
status[eStatus::POISON] = 0;
status[eStatus::BLESS_CURSE] = 8;
status[eStatus::HASTE_SLOW] = 8;
status[eStatus::WEBS] = 0;
status[eStatus::DISEASE] = 0;
if(status[eStatus::DUMB] > 0)
status[eStatus::DUMB] = 0;
status[eStatus::MARTYRS_SHIELD] = 8;
}
void cCreature::heal(int amt) {
if(!is_alive()) return;
if(health >= m_health) return;
health += amt;
if(health > m_health)
health = m_health;
if(health < 0)
health = 0;
}
void cCreature::cure(int amt) {
if(!is_alive()) return;
if(status[eStatus::POISON] <= amt)
status[eStatus::POISON] = 0;
else status[eStatus::POISON] -= amt;
}
void cCreature::restore_sp(int amt) {
if(!is_alive()) return;
if(mp >= max_mp) return;
mp += amt;
if(mp > max_mp)
mp = max_mp;
if(mp < 0) mp = 0;
}
void cCreature::drain_sp(int drain) {
drain = magic_adjust(drain);
if(drain > 0) {
if(mu > 0 && mp > 4)
drain = min(mp, drain / 3);
else if(cl > 0 && mp > 10)
drain = min(mp, drain / 2);
mp -= drain;
if(mp < 0) mp = 0;
}
}
void cCreature::poison(int how_much) {
if(how_much != 0) {
how_much *= resist[eDamageType::POISON];
how_much /= 100;
}
apply_status(eStatus::POISON, how_much);
if(how_much >= 0)
spell_note((how_much == 0) ? 10 : 4);
else
spell_note(34);
}
void cCreature::acid(int how_much) {
how_much = magic_adjust(how_much);
apply_status(eStatus::ACID, how_much);
if(how_much >= 0)
spell_note(31);
else
spell_note(48);
}
void cCreature::slow(int how_much) {
how_much = magic_adjust(how_much);
apply_status(eStatus::HASTE_SLOW, -how_much);
if(how_much >= 0)
spell_note((how_much == 0) ? 10 : 2);
else
spell_note(35);
}
void cCreature::curse(int how_much) {
how_much = magic_adjust(how_much);
apply_status(eStatus::BLESS_CURSE, -how_much);
if(how_much >= 0)
spell_note((how_much == 0) ? 10 : 5);
else
spell_note(36);
}
void cCreature::web(int how_much) {
how_much = magic_adjust(how_much);
apply_status(eStatus::WEBS, how_much);
if(how_much >= 0)
spell_note((how_much == 0) ? 10 : 19);
else
spell_note(37);
}
void cCreature::scare(int how_much) {
how_much = magic_adjust(how_much);
morale -= how_much;
if(how_much >= 0)
spell_note((how_much == 0) ? 10 : 1);
else
spell_note(47);
}
void cCreature::disease(int how_much) {
how_much = magic_adjust(how_much);
apply_status(eStatus::DISEASE, how_much);
if(how_much >= 0)
spell_note((how_much == 0) ? 10 : 25);
else
spell_note(38);
}
void cCreature::dumbfound(int how_much) {
how_much = magic_adjust(how_much);
apply_status(eStatus::DUMB, how_much);
if(how_much >= 0)
spell_note((how_much == 0) ? 10 : 22);
else
spell_note(39);
}
// For charm, amount is the resulting attitude of the charmed monster; if 0, attitude is 2.
void cCreature::sleep(eStatus which_status,int amount,int penalty) {
if(which_status != eStatus::CHARM && amount < 0) {
status[which_status] -= amount;
if(which_status != eStatus::ASLEEP)
status[which_status] = max(0, status[which_status]);
return;
}
if((which_status == eStatus::ASLEEP) &&
(m_type == eRace::UNDEAD || m_type == eRace::SKELETAL || m_type == eRace::SLIME ||
m_type == eRace::STONE || m_type == eRace::PLANT))
return;
short r1 = get_ran(1,1,100);
if(resist[eDamageType::MAGIC] > 0) {
r1 *= 100;
r1 /= resist[eDamageType::MAGIC];
} else r1 = 200;
r1 += penalty;
if(which_status == eStatus::FORCECAGE && (mu > 0 || cl > 0))
r1 += 5;
if(which_status == eStatus::ASLEEP)
r1 -= 25;
if(which_status == eStatus::PARALYZED)
r1 -= 15;
if(which_status == eStatus::ASLEEP && abil[eMonstAbil::FIELD].active && abil[eMonstAbil::FIELD].gen.fld == eFieldType::CLOUD_SLEEP)
return;
if(r1 > charm_odds[level / 2]) {
//one_sound(68);
spell_note(10);
}
else {
if(which_status == eStatus::CHARM) {
if(amount <= 0 || amount > 3) amount = 2;
attitude = eAttitude(amount);
spell_note(23);
} else if(which_status == eStatus::FORCECAGE) {
status[eStatus::FORCECAGE] = amount;
spell_note(52);
} else {
status[which_status] = amount;
if(which_status == eStatus::ASLEEP && (amount >= 0))
spell_note(28);
if(which_status == eStatus::PARALYZED && (amount >= 0))
spell_note(30);
if(amount < 0)
spell_note(40);
}
//one_sound(53);
}
}
bool cCreature::is_alive() const {
return active > 0;
}
bool cCreature::is_friendly() const {
return attitude == eAttitude::DOCILE || attitude == eAttitude::FRIENDLY;
}
bool cCreature::is_friendly(const iLiving& other) const {
if(is_friendly() != other.is_friendly())
return false;
if(const cCreature* monst = dynamic_cast<const cCreature*>(&other)) {
if(!is_friendly()) return attitude == monst->attitude;
}
// If we get this far, both monsters are friendly to the player.
// (Or, maybe the other is a player rather than a monster.)
return true;
}
location cCreature::get_loc() const {
return cur_loc;
}
bool cCreature::is_shielded() const {
if(status[eStatus::MARTYRS_SHIELD] > 0)
return true;
if(abil[eMonstAbil::MARTYRS_SHIELD].active && get_ran(1,1,1000) <= abil[eMonstAbil::MARTYRS_SHIELD].special.extra1)
return true;
return false;
}
int cCreature::get_shared_dmg(int base_dmg) const {
if(abil[eMonstAbil::MARTYRS_SHIELD].active) {
base_dmg *= abil[eMonstAbil::MARTYRS_SHIELD].special.extra2;
base_dmg /= 100;
}
return base_dmg;
}
int cCreature::magic_adjust(int how_much) {
if(how_much <= 0) return how_much;
if(abil[eMonstAbil::ABSORB_SPELLS].active && get_ran(1,1,1000) <= abil[eMonstAbil::ABSORB_SPELLS].special.extra1) {
int gain = abil[eMonstAbil::ABSORB_SPELLS].special.extra2;
if(32767 - health > gain)
health = 32767;
else health += gain;
return 0;
}
// TODO: Magic resistance status effect?
how_much *= resist[eDamageType::MAGIC];
how_much /= 100;
return how_much;
}
int cCreature::get_health() const {
return health;
}
int cCreature::get_magic() const {
return mp;
}
int cCreature::get_level() const {
return level;
}
bool cCreature::on_space(location loc) const {
if(loc.x - cur_loc.x >= 0 && loc.x - cur_loc.x <= x_width - 1 &&
loc.y - cur_loc.y >= 0 && loc.y - cur_loc.y <= y_width - 1)
return true;
return false;
}
void cCreature::writeTo(std::ostream& file) const {
file << "MONSTER " << number << '\n';
file << "ATTITUDE " << attitude << '\n';
file << "STARTATT " << start_attitude << '\n';
file << "STARTLOC " << start_loc.x << ' ' << start_loc.y << '\n';
file << "LOCATION " << cur_loc.x << ' ' << cur_loc.y << '\n';
file << "MOBILITY " << mobility << '\n';
file << "TIMEFLAG " << time_flag << '\n';
file << "SUMMONED " << summon_time << ' ' << party_summoned << '\n';
file << "SPEC " << spec1 << ' ' << spec2 << '\n';
file << "SPECCODE " << spec_enc_code << '\n';
file << "TIMECODE " << time_code << '\n';
file << "TIME " << monster_time << '\n';
file << "TALK " << personality << '\n';
file << "DEATH " << special_on_kill << '\n';
file << "FACE " << facial_pic << '\n';
file << "TARGET " << target << '\n';
file << "TARGLOC " << targ_loc.x << ' ' << targ_loc.y << '\n';
for(auto stat : status) {
if(stat.second != 0)
file << "STATUS " << stat.first << ' ' << stat.second << '\n';
}
file << "CURHP " << health << '\n';
file << "CURSP " << mp << '\n';
file << "MORALE " << morale << '\n';
file << "DIRECTION " << direction << '\n';
// TODO: Should we be saving "max_mp" and/or "m_morale"?
}
void cCreature::readFrom(std::istream& file) {
while(file) {
std::string cur;
getline(file, cur);
std::istringstream line(cur);
line >> cur;
if(cur == "MONSTER")
line >> number;
else if(cur == "ATTITUDE")
line >> attitude;
else if(cur == "STARTATT") {
line >> start_attitude;
} else if(cur == "STARTLOC")
line >> start_loc.x >> start_loc.y;
else if(cur == "LOCATION")
line >> cur_loc.x >> cur_loc.y;
else if(cur == "MOBILITY") {
unsigned int i;
line >> i;
mobility = i;
} else if(cur == "TIMEFLAG") {
line >> time_flag;
} else if(cur == "SUMMONED")
line >> summon_time >> party_summoned;
else if(cur == "SPEC")
line >> spec1 >> spec2;
else if(cur == "SPECCODE") {
int i;
line >> i;
spec_enc_code = i;
} else if(cur == "TIMECODE") {
int i;
line >> i;
time_code = i;
} else if(cur == "TIME")
line >> monster_time;
else if(cur == "TALK")
line >> personality;
else if(cur == "DEATH")
line >> special_on_kill;
else if(cur == "FACE")
line >> facial_pic;
else if(cur == "TARGET")
line >> target;
else if(cur == "TARGLOC")
line >> targ_loc.x >> targ_loc.y;
else if(cur == "CURHP")
line >> health;
else if(cur == "CURSP")
line >> mp;
else if(cur == "MORALE")
line >> morale;
else if(cur == "DIRECTION")
line >> direction;
else if(cur == "STATUS") {
eStatus i;
line >> i >> status[i];
}
}
}
void cCreature::print_attacks(iLiving* target) {
if(!print_result) return;
std::string msg = m_name;
msg += " attacks ";
if(cPlayer* who = dynamic_cast<cPlayer*>(target))
msg += who->name;
else if(cCreature* monst = dynamic_cast<cCreature*>(target))
msg += monst->m_name;
else msg += "you";
print_result(msg);
}
void cCreature::spell_note(int which_mess) {
if(!print_result) return;
std::string msg = m_name;
switch(which_mess) {
case 1:
msg = " " + msg + " scared.";
break;
case 2:
msg = " " + msg + " slowed.";
break;
case 3:
msg = " " + msg + " weakened.";
break;
case 4:
msg = " " + msg + " poisoned.";
break;
case 5:
msg = " " + msg + " cursed.";
break;
case 6:
msg = " " + msg + " ravaged.";
break;
case 7:
msg = " " + msg + " undamaged.";
break;
case 8:
msg = " " + msg + " is stoned.";
break;
case 9:
msg = " Gazes at " + msg + '.';
break;
case 10:
msg = " " + msg + " resists.";
break;
case 11:
msg = " Drains " + msg + '.';
break;
case 12:
msg = " Shoots at " + msg + '.';
break;
case 13:
msg = " Throws spear at " + msg + '.';
break;
case 14:
msg = " Throws rock at " + msg + '.';
break;
case 15:
msg = " Throws razordisk at " + msg + '.';
break;
case 16:
msg = " Hits " + msg + '.';
break;
case 17:
msg = " " + msg + " disappears.";
break;
case 18:
msg = " Misses " + msg + '.';
break;
case 19:
msg = " " + msg + " is webbed.";
break;
case 20:
msg = " " + msg + " chokes.";
break;
case 21:
msg = " " + msg + " summoned.";
break;
case 22:
msg = " " + msg + " is dumbfounded.";
break;
case 23:
msg = " " + msg + " is charmed.";
break;
case 24:
msg = " " + msg + " is recorded.";
break;
case 25:
msg = " " + msg + " is diseased.";
break;
case 26:
msg = " " + msg + " is an avatar!";
break;
case 27:
msg = " " + msg + " splits!";
break;
case 28:
msg = " " + msg + " falls asleep.";
break;
case 29:
msg = " " + msg + " wakes up.";
break;
case 30:
msg = " " + msg + " paralyzed.";
break;
case 31:
msg = " " + msg + " covered with acid.";
break;
case 32:
msg = " Fires spines at " + msg + '.';
break;
case 33:
msg = " " + msg + " summons aid.";
break;
case 34:
msg = " " + msg + " is cured.";
break;
case 35:
msg = " " + msg + " is hasted.";
break;
case 36:
msg = " " + msg + " is blessed.";
break;
case 37:
msg = " " + msg + " cleans webs.";
break;
case 38:
msg = " " + msg + " feels better.";
break;
case 39:
msg = " " + msg + " mind cleared.";
break;
case 40:
msg = " " + msg + " feels alert.";
break;
case 41:
msg = " " + msg + " is healed.";
break;
case 42:
msg = " " + msg + " drained of health.";
break;
case 43:
msg = " " + msg + " magic recharged.";
break;
case 44:
msg = " " + msg + " drained of magic.";
break;
case 45:
msg = " " + msg + " returns to life!";
break;
case 46:
msg = " " + msg + " dies.";
break;
case 47:
msg = " " + msg + " rallies its courage.";
break;
case 48:
msg = " " + msg + " cleans off acid.";
break;
case 49:
msg = " " + msg + " breaks barrier.";
break;
case 50:
msg = " " + msg + " breaks force cage.";
break;
case 51:
msg = " " + msg + " is obliterated!";
break;
case 52:
msg = " " + msg + " is trapped!";
break;
case 53:
msg = " Throws dart at " + msg + '.';
break;
case 54:
msg = " Throws knife at " + msg + '.';
break;
case 55:
msg = " Fires ray at " + msg + '.';
break;
case 56:
msg = " Gazes at " + msg + '.';
break;
case 57:
msg = " Breathes on " + msg + '.';
break;
case 58:
msg = " Throws web at " + msg + '.';
break;
case 59:
msg = " Spits at " + msg + '.';
break;
default:
msg += ": Unknown action " + std::to_string(which_mess);
}
if(which_mess > 0)
print_result((char *) msg.c_str());
}
void cCreature::cast_spell_note(eSpell spell) {
if(!print_result) return;
print_result(m_name + " casts:");
print_result(" " + (*spell).name());
}
void cCreature::breathe_note() {
if(!print_result) return;
print_result(m_name + " breathes.");
}
void cCreature::damaged_msg(int how_much,int how_much_spec) {
if(!print_result) return;
std::ostringstream sout;
sout << " " << m_name << " takes " << how_much;
if(how_much_spec > 0)
sout << '+' << how_much_spec;
print_result(sout.str());
}
void cCreature::killed_msg() {
if(!print_result) return;
print_result(" " + m_name + " dies.");
}

76
src/universe/creature.hpp Normal file
View File

@@ -0,0 +1,76 @@
//
// creature.hpp
// BoE
//
// Created by Celtic Minstrel on 15-01-24.
//
//
#ifndef BoE_creature_hpp
#define BoE_creature_hpp
#include <iosfwd>
#include "location.hpp"
#include "monster.hpp"
#include "living.hpp"
class cCreature : public cMonster, public cTownperson, public iLiving {
public:
static const short charm_odds[21];
short active = 0;
eAttitude attitude;
location cur_loc;
short summon_time = 0;
bool party_summoned;
short target = 6;
location targ_loc;
short health = 0;
short mp = 0;
short max_mp = 0;
short morale = 0,m_morale = 0; // these are calculated in-game based on the level
cCreature();
cCreature(int num);
void heal(int how_much);
void poison(int how_much);
void cure(int how_much);
void acid(int how_much);
void curse(int how_much);
void slow(int how_much);
void web(int how_much);
void disease(int how_much);
void dumbfound(int how_much);
void scare(int how_much);
void sleep(eStatus type, int how_much, int adj);
void avatar();
void drain_sp(int how_much);
void restore_sp(int how_much);
int get_health() const;
int get_magic() const;
int get_level() const;
bool is_alive() const;
bool is_friendly() const;
bool is_friendly(const iLiving& other) const;
bool is_shielded() const;
int get_shared_dmg(int base_dmg) const;
location get_loc() const;
int magic_adjust(int base);
void spell_note(int which);
void cast_spell_note(eSpell spell);
void print_attacks(iLiving* target);
void breathe_note();
void damaged_msg(int how_much, int extra);
void killed_msg();
bool on_space(location loc) const;
void import_legacy(legacy::creature_data_type old);
void writeTo(std::ostream& file) const;
void readFrom(std::istream& file);
};
#endif

75
src/universe/living.cpp Normal file
View File

@@ -0,0 +1,75 @@
//
// living.cpp
// BoE
//
// Created by Celtic Minstrel on 15-01-24.
//
//
#include "living.hpp"
#include <set>
#include <algorithm>
#include "mathutil.hpp"
void iLiving::apply_status(eStatus which, int how_much) {
if(!is_alive()) return;
static const std::set<eStatus> allow_negative = {
// The obvious ones:
eStatus::BLESS_CURSE, eStatus::HASTE_SLOW,
// The ones that BoE previously allowed:
eStatus::POISONED_WEAPON, eStatus::POISON, eStatus::ASLEEP,
// (Note: Negative levels of sleep can be obtained from the Hyperactivity spell. The other two never go negative.)
// The additional ones that make sense in the negative:
eStatus::MAGIC_RESISTANCE, eStatus::DUMB,
};
int lo = 0, hi = 8;
if(which == eStatus::MARTYRS_SHIELD)
hi = 10;
else if(which == eStatus::PARALYZED)
hi = 5000;
else if(which == eStatus::FORCECAGE)
hi = 1000;
if(allow_negative.count(which))
lo = -hi;
if(which == eStatus::ASLEEP || which == eStatus::DUMB) {
// No "wrapping" allowed for these effects.
if(status[which] < 0) hi = 0;
else if(status[which] > 0) lo = 0;
}
status[which] = minmax(lo,hi,status[which] + how_much);
}
void iLiving::clear_bad_status() {
std::map<eStatus, short> old;
status.swap(old);
std::remove_copy_if(old.begin(), old.end(), std::inserter(status, status.begin()), [](std::pair<const eStatus, short> kv) {
return isStatusNegative(kv.first) ? kv.second > 0 : kv.second < 0;
});
}
void iLiving::clear_brief_status() {
std::map<eStatus, short> old;
status.swap(old);
std::remove_copy_if(old.begin(), old.end(), std::inserter(status, status.begin()), [](std::pair<const eStatus, short> kv) -> bool {
if(kv.first == eStatus::POISON) return false;
if(kv.first == eStatus::DISEASE) return false;
if(kv.first == eStatus::DUMB) return false;
if(kv.first == eStatus::ACID && kv.second > 2)
return false;
return true;
});
}
void iLiving::void_sanctuary() {
if(status[eStatus::INVISIBLE] > 0)
status[eStatus::INVISIBLE] = 0;
}
void(* iLiving::print_result)(std::string) = nullptr;

62
src/universe/living.hpp Normal file
View File

@@ -0,0 +1,62 @@
//
// living.hpp
// BoE
//
// Created by Celtic Minstrel on 15-01-24.
//
//
#ifndef BoE_living_hpp
#define BoE_living_hpp
#include <map>
#include <string>
#include "location.hpp"
#include "damage.hpp"
class iLiving {
public:
// HACK: This is only really marked mutable so that I can use operator[] from const methods
mutable std::map<eStatus,short> status;
short ap = 0;
eDirection direction = DIR_HERE;
short marked_damage = 0; // for use during animations
virtual bool is_alive() const = 0;
virtual bool is_friendly() const = 0; // Return true if friendly to the party.
virtual bool is_friendly(const iLiving& other) const = 0; // Return true if friendly to living entity
virtual bool is_shielded() const = 0; // Checks for martyr's shield in any form - status, monster abil, item abil
virtual int get_shared_dmg(int base_dmg) const = 0; // And this goes with the above.
virtual void apply_status(eStatus which, int how_much);
virtual void clear_brief_status();
virtual void clear_bad_status();
virtual void void_sanctuary();
virtual void heal(int how_much) = 0;
virtual void poison(int how_much) = 0;
virtual void cure(int how_much) = 0;
virtual void acid(int how_much) = 0;
virtual void curse(int how_much) = 0;
virtual void slow(int how_much) = 0;
virtual void web(int how_much) = 0;
virtual void disease(int how_much) = 0;
virtual void dumbfound(int how_much) = 0;
virtual void scare(int how_much) = 0;
virtual void sleep(eStatus type, int how_much, int adj) = 0; // Also handles paralysis, charm, and forcecage
virtual void avatar() = 0;
virtual void drain_sp(int how_much) = 0;
virtual void restore_sp(int how_much) = 0;
virtual int get_health() const = 0;
virtual int get_magic() const = 0;
virtual int get_level() const = 0;
virtual location get_loc() const = 0;
virtual ~iLiving() = default;
static void(* print_result)(std::string);
};
#endif

1269
src/universe/party.cpp Normal file

File diff suppressed because it is too large Load Diff

237
src/universe/party.hpp Normal file
View File

@@ -0,0 +1,237 @@
/*
* party.h
* BoE
*
* Created by Celtic Minstrel on 24/04/09.
*
*/
#ifndef BOE_DATA_PARTY_H
#define BOE_DATA_PARTY_H
#include <string>
#include <vector>
#include <array>
#include <map>
#include <set>
#include <boost/iterator/indirect_iterator.hpp>
#include "vehicle.hpp"
#include "population.hpp"
#include "item.hpp"
#include "pc.hpp"
#include "outdoors.hpp"
#include "monster.hpp"
#include "living.hpp"
#include "quest.hpp"
namespace legacy {
struct party_record_type;
struct big_tr_type;
struct stored_items_list_type;
struct talk_save_type;
struct creature_list_type;
struct pc_record_type;
struct setup_save_type;
};
struct campaign_flag_type{
unsigned char idx[25][25];
};
struct job_bank_t {
std::array<int,6> jobs;
int anger = 0;
bool inited = false;
};
enum eEncNoteType {
NOTE_SCEN,
NOTE_OUT,
NOTE_TOWN,
};
class cUniverse;
class cItem;
class cParty : public iLiving {
public:
class cConvers { // conversation; formerly talk_save_type
public:
bool filled = false;
std::string who_said, in_town, the_str1, the_str2, in_scen;
void import_legacy(legacy::talk_save_type old, const cScenario& scenario);
};
class cJournal {
public:
unsigned short day;
std::string the_str, in_scen;
};
class cEncNote {
public:
eEncNoteType type;
std::string the_str, where, in_scen;
void import_legacy(int16_t(& old)[2], const cScenario& scenario);
};
// formerly party_record_type
// TODO: Should we make age a long long?
long next_pc_id = 1000;
unsigned long age;
unsigned short gold;
unsigned short food;
unsigned char stuff_done[350][50];
// These used to be stored as magic SDFs
unsigned char hostiles_present;
bool easy_mode = false, less_wm = false;
// End former magic SDFs
std::array<unsigned char,90> magic_ptrs;
short light_level;
location outdoor_corner;
location i_w_c;
// TODO: Does this duplicate cCurTown::p_loc? If not, why not?
location out_loc, town_loc;
location loc_in_sec;
short town_num;
std::vector<cVehicle> boats;
std::vector<cVehicle> horses;
std::array<cPopulation,4> creature_save;
short in_boat;
short in_horse;
std::array<cOutdoors::cCreature,10> out_c;
std::map<int,std::array<cItem,30>> magic_store_items;
std::map<int,std::map<int,int>> store_limited_stock;
std::vector<job_bank_t> job_banks;
std::array<mon_num_t,4> imprisoned_monst; // Soul Crystal
std::set<mon_num_t> m_noted; // has the monster been scried?
std::set<mon_num_t> m_seen; // has the monster ever been seen? (this used to have the above meaning)
std::vector<cJournal> journal;
std::vector<cEncNote> special_notes;
std::vector<cConvers> talk_save;
std::map<ePartyStatus,short> status;
std::map<int, cJob> active_quests;
location left_at;
size_t left_in;
eDirection direction;
short at_which_save_slot;
std::bitset<20> alchemy;
std::map<int,int> key_times;
std::vector<cTimer> party_event_timers;
std::set<int> spec_items;
long long total_m_killed, total_dam_done, total_xp_gained, total_dam_taken;
std::string scen_name;
private:
std::array<cPlayer*,6> adven;
public:
unsigned short setup[4][64][64]; // formerly setup_save_type
std::array<std::vector<cItem>,3> stored_items; // formerly stored_items_list_type
std::vector<cMonster> summons; // an array of monsters which can be summoned by the party's items yet don't originate from this scenario
unsigned short scen_won, scen_played; // numbers of scenarios won and played respectively by this party
std::map<std::string,campaign_flag_type> campaign_flags;
private:
std::map<unsigned short,std::pair<unsigned short,unsigned char>> pointers;
using sd_array = decltype(stuff_done);
public:
static const int sdx_max = std::extent<sd_array, 0>::value - 1;
static const int sdy_max = std::extent<sd_array, 1>::value - 1;
void set_ptr(unsigned short p, unsigned short sdfx, unsigned short sdfy);
void force_ptr(unsigned short p, unsigned short val);
void clear_ptr(unsigned short p);
unsigned char get_ptr(unsigned short p);
void import_legacy(legacy::party_record_type& old, cUniverse& univ);
void import_legacy(legacy::big_tr_type& old);
void import_legacy(legacy::stored_items_list_type& old,short which_list);
void import_legacy(legacy::setup_save_type& old);
void import_legacy(legacy::pc_record_type(& old)[6]);
bool is_alive() const;
bool is_friendly() const;
bool is_friendly(const iLiving& other) const;
bool is_shielded() const;
int get_shared_dmg(int base_dmg) const;
location get_loc() const;
void apply_status(eStatus which, int how_much);
void heal(int how_much);
void poison(int how_much);
void cure(int how_much);
void acid(int how_much);
void curse(int how_much);
void slow(int how_much);
void web(int how_much);
void disease(int how_much);
void dumbfound(int how_much);
void scare(int how_much);
void sleep(eStatus type, int how_much, int adj);
void clear_bad_status();
void avatar();
void drain_sp(int how_much);
void restore_sp(int how_much);
int get_health() const;
int get_magic() const;
int get_level() const;
int calc_day() const;
void new_pc(size_t spot);
void replace_pc(size_t spot, cPlayer* with);
size_t free_space();
size_t count(eMainStatus type = eMainStatus::ALIVE);
void void_pcs();
bool save_talk(const std::string& who, const std::string& where, const std::string& str1, const std::string& str2);
bool add_to_journal(const std::string& event, short day);
bool record(eEncNoteType type, const std::string& what, const std::string& where);
bool start_timer(short time, short node, short type);
cPlayer& operator[](unsigned short n);
const cPlayer& operator[](unsigned short n) const;
void writeTo(std::ostream& file, const cScenario& scen) const;
void readFrom(std::istream& file, cScenario& scen);
bool give_item(cItem item,int flags);
bool forced_give(cItem item,eItemAbil abil,short dat = -1);
bool has_abil(eItemAbil abil, short dat = -1);
bool take_abil(eItemAbil abil, short dat = -1);
bool check_class(unsigned int item_class,bool take);
bool start_split(short x, short y, snd_num_t noise, short who);
bool end_split(snd_num_t noise);
bool is_split() const;
bool pc_present(short n) const;
iLiving& pc_present() const; // If only one pc is present, returns that pc. Otherwise returns party.
void swap_pcs(size_t a, size_t b);
bool sd_legit(short a, short b);
auto begin() -> boost::indirect_iterator<decltype(adven)::iterator> {
return boost::make_indirect_iterator(adven.begin());
}
auto end() -> boost::indirect_iterator<decltype(adven)::iterator> {
return boost::make_indirect_iterator(adven.end());
}
typedef std::vector<cEncNote>::iterator encIter;
typedef std::vector<cJournal>::iterator journalIter;
typedef std::vector<cConvers>::iterator talkIter;
cParty(long party_preset = 'dflt');
~cParty();
// Copy-and-swap
void swap(cParty& other);
cParty(const cParty& other);
cParty(cParty&& other);
cParty& operator=(cParty other);
};
bool operator==(const cParty::cConvers& one, const cParty::cConvers& two);
bool operator==(const cParty::cJournal& one, const cParty::cJournal& two);
bool operator==(const cParty::cEncNote& one, const cParty::cEncNote& two);
std::istream& operator>>(std::istream& in, eEncNoteType& type);
std::ostream& operator<<(std::ostream& out, eEncNoteType type);
#endif

1323
src/universe/pc.cpp Normal file

File diff suppressed because it is too large Load Diff

179
src/universe/pc.hpp Normal file
View File

@@ -0,0 +1,179 @@
/*
* pc.h
* BoE
*
* Created by Celtic Minstrel on 24/04/09.
*
*/
#ifndef BOE_DATA_PC_H
#define BOE_DATA_PC_H
#include <string>
#include <map>
#include <iosfwd>
#include <array>
#include <bitset>
#include "item.hpp"
#include "pictypes.hpp"
#include "living.hpp"
#include "skills_traits.hpp"
#include "race.hpp"
#include "spell.hpp"
namespace legacy { struct pc_record_type; };
enum class eBuyStatus {OK, NO_SPACE, NEED_GOLD, TOO_HEAVY, HAVE_LOTS};
enum {
GIVE_DO_PRINT = 1,
GIVE_ALLOW_OVERLOAD = 2,
// These three are mutually exclusive:
GIVE_EQUIP_SOFT = 4,
GIVE_EQUIP_TRY = 8,
GIVE_EQUIP_FORCE = 12,
};
class cParty;
class cPlayer;
struct cInvenSlot {
unsigned int slot;
explicit cInvenSlot(cPlayer& owner) : slot(std::numeric_limits<unsigned int>::max()), owner(owner) {}
cInvenSlot(cPlayer& owner, int slot) : slot(slot), owner(owner) {}
void clear() {
slot = std::numeric_limits<unsigned int>::max();
}
explicit operator bool() const;
bool operator !() const;
cItem* operator->();
const cItem* operator->() const;
cItem& operator*();
const cItem& operator*() const;
friend bool operator==(const cInvenSlot& a, const cInvenSlot& b) {
return &a.owner == &b.owner && a.slot == b.slot;
}
private:
cPlayer& owner;
};
inline bool operator!=(const cInvenSlot& a, const cInvenSlot& b) {
return !(a == b);
}
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;
// HACK: This is only really marked mutable so that I can use operator[] from const methods
mutable std::map<eSkill, short> skills;
unsigned short max_health;
short cur_health;
unsigned short max_sp;
short cur_sp;
unsigned short experience;
short skill_pts;
short level;
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;
cInvenSlot weap_poisoned;
// HACK: This is only really marked mutable so that I can use operator[] from const methods
mutable std::map<eTrait,bool> traits;
eRace race;
long unique_id;
// transient stuff
std::map<eSkill,eSpell> last_cast;
location combat_pos;
short parry = 0;
iLiving* last_attacked = nullptr; // Note: Currently this is assigned but never read
bool is_alive() const;
bool is_friendly(const iLiving& other) const;
bool is_friendly() const;
bool is_shielded() const;
int get_shared_dmg(int base_dmg) const;
int get_health() const;
int get_magic() const;
int get_level() const;
location get_loc() const;
void finish_create();
void void_sanctuary();
void heal(int how_much);
void poison(int how_much);
void cure(int how_much);
void acid(int how_much);
void curse(int how_much);
void slow(int how_much);
void web(int how_much);
void disease(int how_much);
void dumbfound(int how_much);
void scare(int how_much);
void sleep(eStatus type, int how_much, int adj);
void avatar();
void drain_sp(int how_much);
void restore_sp(int how_much);
void combine_things();
void sort_items();
bool give_item(cItem item, int flags);
bool equip_item(int which_item, bool do_print);
bool unequip_item(int which_item, bool do_print);
std::pair<cInvenSlot, cInvenSlot> get_weapons();
void take_item(int which_item);
void remove_charge(int which_item);
const cInvenSlot has_space() const;
cInvenSlot has_space();
short max_weight() const;
short cur_weight() const;
short free_weight() const;
short get_prot_level(eItemAbil abil, short dat = -1) const;
const cInvenSlot has_abil_equip(eItemAbil abil, short dat = -1) const;
cInvenSlot has_abil_equip(eItemAbil abil, short dat = -1);
const cInvenSlot has_abil(eItemAbil abil, short dat = -1) const;
cInvenSlot has_abil(eItemAbil abil, short dat = -1);
const cInvenSlot has_type_equip(eItemType type) const;
cInvenSlot has_type_equip(eItemType type);
const cInvenSlot has_type(eItemType type) const;
cInvenSlot has_type(eItemType type);
const cInvenSlot has_class_equip(unsigned int item_class) const;
cInvenSlot has_class_equip(unsigned int item_class);
const cInvenSlot has_class(unsigned int item_class) const;
cInvenSlot has_class(unsigned int item_class);
short skill(eSkill skill) const;
short stat_adj(eSkill skill) const;
eBuyStatus ok_to_buy(short cost,cItem item) const;
void join_party(cParty& p) {party = &p;}
cPlayer* leave_party() {party = nullptr; return this;}
void import_legacy(legacy::pc_record_type old);
cPlayer(cParty& party);
cPlayer(cParty& party,long key,short slot);
short get_tnl();
void writeTo(std::ostream& file) const;
void readFrom(std::istream& file);
virtual ~cPlayer() = default;
// Copy-and-swap
void swap(cPlayer& other);
cPlayer(const cPlayer& other);
cPlayer(cPlayer&& other);
// For now, not assignable because of an issue of how to handle the unique_id
cPlayer& operator=(cPlayer other) = delete;
};
#endif

View File

@@ -0,0 +1,79 @@
/*
* creatlist.cpp
* BoE
*
* Created by Celtic Minstrel on 24/04/09.
*
*/
#include "population.hpp"
#include <string>
#include <vector>
#include <map>
#include <sstream>
#include "oldstructs.hpp"
void cPopulation::import_legacy(legacy::creature_list_type old){
dudes.resize(60);
for(int i = 0; i < 60; i++)
dudes[i].import_legacy(old.dudes[i]);
which_town = old.which_town;
hostile = old.hostile;
}
const cCreature& cPopulation::operator[](size_t n) const {
return dudes[n];
}
cCreature& cPopulation::operator[](size_t n){
return dudes[n];
}
void cPopulation::init(size_t n) {
if(n >= dudes.size()) dudes.resize(n + 1);
dudes[n].active = 1;
}
// This function combines a cTownperson from a scenario town record with a cMonster from the scenario record
// into a cCreature, and prepares it for use in-game according to the user's preferences and party strength
// replaces return_monster_template() from boe.monsters.cpp
void cPopulation::assign(size_t n, const cTownperson& other, const cMonster& base, bool easy, int difficulty_adjust){
// Make sure the space exists
if(n >= dudes.size()) dudes.resize(n + 1);
// First copy over the superclass fields
static_cast<cTownperson&>(dudes[n]) = other;
static_cast<cMonster&>(dudes[n]) = base;
// Now set up extra stuff
dudes[n].active = 1; // TODO: Is this right?
if(dudes[n].invisible) dudes[n].picture_num = 0;
dudes[n].m_health /= easy ? 2 : 1;
dudes[n].m_health *= difficulty_adjust;
dudes[n].health = dudes[n].m_health;
dudes[n].ap = 0;
if(dudes[n].mu > 0 || dudes[n].cl > 0)
dudes[n].max_mp = dudes[n].mp = 12 * dudes[n].level;
else dudes[n].max_mp = dudes[n].mp = 0;
dudes[n].m_morale = 10 * dudes[n].level;
if(dudes[n].level > 20)
dudes[n].m_morale += 10 * (dudes[n].level - 20);
dudes[n].morale = dudes[n].m_morale;
dudes[n].direction = DIR_HERE;
dudes[n].status.clear();
dudes[n].attitude = dudes[n].start_attitude;
dudes[n].cur_loc = dudes[n].start_loc;
dudes[n].target = 6; // No target
dudes[n].summon_time = 0;
}
void cPopulation::swap(cPopulation& other) {
std::swap(dudes, other.dudes);
std::swap(which_town, other.which_town);
std::swap(hostile, other.hostile);
}
void cPopulation::readFrom(std::istream& in, size_t n) {
if(n >= dudes.size()) dudes.resize(n + 1);
dudes[n].readFrom(in);
}

View File

@@ -0,0 +1,43 @@
/*
* creatlist.h
* BoE
*
* Created by Celtic Minstrel on 24/04/09.
*
*/
#ifndef BOE_DATA_CREATLIST_H
#define BOE_DATA_CREATLIST_H
#include "monster.hpp"
#include <iosfwd>
#include "creature.hpp"
namespace legacy {
struct creature_list_type;
struct creature_data_type;
};
class cPopulation {
std::vector<cCreature> dudes;
public:
short which_town;
bool hostile;
void import_legacy(legacy::creature_list_type old);
void init(size_t n);
void assign(size_t n, const cTownperson& other, const cMonster& base, bool easy, int difficulty_adjust);
void readFrom(std::istream& in, size_t n);
size_t size() const {return dudes.size();}
void clear() {dudes.clear();}
cCreature& operator[](size_t n);
const cCreature& operator[](size_t n) const;
cPopulation() : which_town(200) {}
std::vector<cCreature>::iterator begin() {return dudes.begin();}
std::vector<cCreature>::iterator end() {return dudes.end();}
// Apparently Visual Studio needs this to work
cPopulation& operator=(const cPopulation& other) = default;
void swap(cPopulation& other);
};
#endif

1456
src/universe/universe.cpp Normal file

File diff suppressed because it is too large Load Diff

223
src/universe/universe.hpp Normal file
View File

@@ -0,0 +1,223 @@
/*
* universe.h
* BoE
*
* Created by Celtic Minstrel on 24/04/09.
*
*/
#ifndef BOE_DATA_UNIVERSE_H
#define BOE_DATA_UNIVERSE_H
#include <iosfwd>
#include <memory>
#include <set>
#include <array>
#include <boost/filesystem/path.hpp>
#include "party.hpp"
#include "population.hpp"
#include "item.hpp"
#include "town.hpp"
#include "talking.hpp"
#include "scenario.hpp"
#include "pictypes.hpp"
namespace legacy {
struct out_info_type;
struct current_town_type;
struct town_item_list;
struct stored_town_maps_type;
struct stored_outdoor_maps_type;
struct big_tr_type;
};
class cCurTown {
short cur_talk_loaded = -1;
bool free_for_sfx(short x, short y);
cUniverse& univ;
cTown* arena;
cTown*const record() const;
public:
bool quickfire_present = false, belt_present = false;
// formerly current_town_type
short difficulty;
cPopulation monst;
std::vector<cItem> items; // formerly town_item_list type
unsigned long fields[64][64];
void import_legacy(legacy::current_town_type& old);
void import_legacy(legacy::town_item_list& old);
void import_legacy(unsigned char(& old_sfx)[64][64], unsigned char(& old_misc_i)[64][64]);
void import_legacy(legacy::big_tr_type& old);
cTown* operator -> ();
explicit cCurTown(cUniverse& univ);
short countMonsters();
cSpeech& cur_talk(); // Get the currently loaded speech
bool prep_talk(short which); // Prepare for loading specified speech, returning true if already loaded
void prep_arena(); // Set up for a combat arena
void place_preset_fields();
bool is_explored(short x, short y) const;
bool is_force_wall(short x, short y) const;
bool is_fire_wall(short x, short y) const;
bool is_antimagic(short x, short y) const;
bool is_scloud(short x, short y) const; // stinking cloud
bool is_ice_wall(short x, short y) const;
bool is_blade_wall(short x, short y) const;
bool is_sleep_cloud(short x, short y) const;
bool is_block(short x, short y) const; // currently unused
bool is_spot(short x, short y) const;
bool is_special(short x, short y) const;
bool is_web(short x, short y) const;
bool is_crate(short x, short y) const;
bool is_barrel(short x, short y) const;
bool is_fire_barr(short x, short y) const;
bool is_force_barr(short x, short y) const;
bool is_quickfire(short x, short y) const;
bool is_sm_blood(short x, short y) const;
bool is_med_blood(short x, short y) const;
bool is_lg_blood(short x, short y) const;
bool is_sm_slime(short x, short y) const;
bool is_lg_slime(short x, short y) const;
bool is_ash(short x, short y) const;
bool is_bones(short x, short y) const;
bool is_rubble(short x, short y) const;
bool is_force_cage(short x, short y) const;
bool is_road(short x, short y) const;
bool set_explored(short x, short y, bool b);
bool set_force_wall(short x, short y, bool b);
bool set_fire_wall(short x, short y, bool b);
bool set_antimagic(short x, short y, bool b);
bool set_scloud(short x, short y, bool b); // stinking cloud
bool set_ice_wall(short x, short y, bool b);
bool set_blade_wall(short x, short y, bool b);
bool set_sleep_cloud(short x, short y, bool b);
bool set_block(short x, short y, bool b); // currently unused
bool set_spot(short x, short y, bool b);
bool set_web(short x, short y, bool b);
bool set_crate(short x, short y, bool b);
bool set_barrel(short x, short y, bool b);
bool set_fire_barr(short x, short y, bool b);
bool set_force_barr(short x, short y, bool b);
bool set_quickfire(short x, short y, bool b);
bool set_sm_blood(short x, short y, bool b);
bool set_med_blood(short x, short y, bool b);
bool set_lg_blood(short x, short y, bool b);
bool set_sm_slime(short x, short y, bool b);
bool set_lg_slime(short x, short y, bool b);
bool set_ash(short x, short y, bool b);
bool set_bones(short x, short y, bool b);
bool set_rubble(short x, short y, bool b);
bool set_force_cage(short x, short y, bool b);
bool set_road(short x, short y, bool b);
bool is_impassable(short x, short y);
void writeTo(std::ostream& file) const;
void readFrom(std::istream& file);
~cCurTown();
// It's not directly copyable due to the cUniverse reference, which must always point to the cUniverse that contains it.
// The cUniverse copy constructor is thus responsible for performing the copy.
cCurTown(const cCurTown&) = delete;
cCurTown& operator=(const cCurTown&) = delete;
// Not movable for similar reasons
cCurTown(const cCurTown&& other) = delete;
cCurTown& operator=(const cCurTown&& other) = delete;
// This implements the actual copy/move.
void copy(const cCurTown& other);
void swap(cCurTown& other);
};
class cCurOut {
cUniverse& univ;
public:
char expl[96][96]; // formerly out_info_type
ter_num_t out[96][96];
unsigned char out_e[96][96];
// These take global coords (ie 0..95)
bool is_spot(int x, int y);
bool is_road(int x, int y);
void import_legacy(legacy::out_info_type& old);
typedef ter_num_t arr_96[96];
arr_96& operator [] (size_t i);
ter_num_t& operator [] (location loc);
void writeTo(std::ostream& file) const;
void readFrom(std::istream& file);
cOutdoors* operator->();
explicit cCurOut(cUniverse& univ);
// It's not directly copyable due to the cUniverse reference, which must always point to the cUniverse that contains it.
// The cUniverse copy constructor is thus responsible for performing the copy.
cCurOut(const cCurOut&) = delete;
cCurOut& operator=(const cCurOut&) = delete;
// Not movable for similar reasons
cCurOut(const cCurOut&& other) = delete;
cCurOut& operator=(const cCurOut&& other) = delete;
// This implements the actual copy/move.
void copy(const cCurOut& other);
void swap(cCurOut& other);
};
enum eTargetType {TARG_ANY, TARG_PC, TARG_MONST};
class cUniverse{
template<typename T> using update_info = std::set<T*>;
// All these maps are transient data that doesn't need to be saved
std::map<pic_num_t, update_info<cItem>> update_items;
std::map<pic_num_t, update_info<cMonster>> update_monsters;
std::map<pic_num_t, update_info<cPlayer>> update_pcs;
std::map<pic_num_t, update_info<miss_num_t>> update_missiles;
std::set<pic_num_t> used_graphics;
pic_num_t addGraphic(pic_num_t pic, ePicType type);
void check_monst(cMonster& monst);
void check_item(cItem& item);
// The string buffer currently isn't saved
std::string strbuf;
std::map<int,std::string> extrabufs;
cItem get_random_store_item(int loot_type, bool allow_junk_treasure);
public:
void exportSummons();
void exportGraphics();
iLiving& get_target(size_t which);
iLiving* target_there(location pos, eTargetType type = TARG_ANY);
size_t get_target_i(iLiving& who);
std::string& get_buf() {return strbuf;}
void swap_buf(int newbuf) {std::swap(strbuf, extrabufs[newbuf]);}
unsigned char& cpn_flag(unsigned int x, unsigned int y, std::string id = "");
short cur_pc = 0;
cPlayer& current_pc();
cScenario scenario;
cParty party;
std::map<long,cPlayer*> stored_pcs;
cCurTown town;
cCurOut out;
fs::path file;
bool debug_mode, ghost_mode, node_step_through;
void clear_stored_pcs();
void import_legacy(legacy::stored_town_maps_type& old);
void import_legacy(legacy::stored_outdoor_maps_type& old);
void enter_scenario(const std::string& name);
void refresh_store_items();
void generate_job_bank(int which, job_bank_t& bank);
short difficulty_adjust() const;
explicit cUniverse(long party_type = 'dflt');
~cUniverse();
// Copy-and-swap
void swap(cUniverse& other);
cUniverse(const cUniverse& other);
cUniverse(cUniverse&& other);
cUniverse& operator=(cUniverse other);
static void(* print_result)(std::string);
};
#endif