diff --git a/CMakeLists.txt b/CMakeLists.txt index bd5bbd19..8b3a713f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -384,6 +384,7 @@ set(GAME_HEADERS src/game/player.h src/game/portraits.h src/game/room.h + src/game/rp/classes.h src/game/rp/types.h src/game/script/routines.h src/game/script/util.h @@ -456,6 +457,7 @@ set(GAME_SOURCES src/game/player.cpp src/game/portraits.cpp src/game/room.cpp + src/game/rp/classes.cpp src/game/script/routines.cpp src/game/script/routines_common.cpp src/game/script/routines_kotor.cpp diff --git a/src/game/blueprint/creature.cpp b/src/game/blueprint/creature.cpp index 0b222709..e9ff7b7d 100644 --- a/src/game/blueprint/creature.cpp +++ b/src/game/blueprint/creature.cpp @@ -40,7 +40,28 @@ void CreatureBlueprint::load(const GffStruct &utc) { _appearance = utc.getInt("Appearance_Type"); _conversation = utc.getString("Conversation"); - _scripts[ScriptType::Spawn] = utc.getString("ScriptSpawn"); + + loadAttributes(utc); + loadScripts(utc); +} + +void CreatureBlueprint::loadAttributes(const GffStruct &utc) { + for (auto &classGff : utc.getList("ClassList")) { + int clazz = classGff.getInt("Class"); + int level = classGff.getInt("ClassLevel"); + _attributes.classLevels.push_back(make_pair(static_cast(clazz), level)); + } + _attributes.abilities[Ability::Strength] = utc.getInt("Str"); + _attributes.abilities[Ability::Dexterity] = utc.getInt("Dex"); + _attributes.abilities[Ability::Constitution] = utc.getInt("Con"); + _attributes.abilities[Ability::Intelligence] = utc.getInt("Int"); + _attributes.abilities[Ability::Wisdom] = utc.getInt("Wis"); + _attributes.abilities[Ability::Charisma] = utc.getInt("Cha"); +} + +void CreatureBlueprint::loadScripts(const GffStruct &utc) { + _onSpawn = utc.getString("ScriptSpawn"); + _onUserDefined = utc.getString("ScriptUserDefine"); } const string &CreatureBlueprint::tag() const { @@ -59,6 +80,18 @@ const string &CreatureBlueprint::conversation() const { return _conversation; } +const CreatureAttributes &CreatureBlueprint::attributes() const { + return _attributes; +} + +const string &CreatureBlueprint::onSpawn() const { + return _onSpawn; +} + +const string &CreatureBlueprint::onUserDefined() const { + return _onUserDefined; +} + } // namespace game } // namespace reone diff --git a/src/game/blueprint/creature.h b/src/game/blueprint/creature.h index 5bee8b3f..819f49cb 100644 --- a/src/game/blueprint/creature.h +++ b/src/game/blueprint/creature.h @@ -23,6 +23,8 @@ #include "../../resource/gfffile.h" +#include "../rp/types.h" + namespace reone { namespace game { @@ -37,32 +39,29 @@ public: const std::vector &equipment() const; int appearance() const; const std::string &conversation() const; + const CreatureAttributes &attributes() const; + const std::string &onSpawn() const; + const std::string &onUserDefined() const; private: - enum class ScriptType { - OnNotice, - SpellAt, - Attacked, - Damaged, - Disturbed, - EndRound, - EndDialogu, - Dialogue, - Spawn, - Rested, - Death, - UserDefine, - OnBlocked - }; - std::string _tag; std::vector _equipment; int _appearance { 0 }; std::string _conversation; - std::unordered_map _scripts; + CreatureAttributes _attributes; + + // Scripts + + std::string _onSpawn; + std::string _onUserDefined; + + // END Scripts CreatureBlueprint(const CreatureBlueprint &) = delete; CreatureBlueprint &operator=(const CreatureBlueprint &) = delete; + + void loadAttributes(const resource::GffStruct &utc); + void loadScripts(const resource::GffStruct &utc); }; } // namespace game diff --git a/src/game/gui/chargen/chargen.cpp b/src/game/gui/chargen/chargen.cpp index 0f4bb73d..a2731bea 100644 --- a/src/game/gui/chargen/chargen.cpp +++ b/src/game/gui/chargen/chargen.cpp @@ -24,6 +24,7 @@ #include "../../game.h" #include "../../portraits.h" +#include "../../rp/classes.h" using namespace std; using namespace std::placeholders; @@ -65,9 +66,7 @@ void CharacterGeneration::load() { hideControl("NEW_LBL"); setControlText("LBL_NAME", ""); - setControlText("LBL_CLASS", ""); - setControlText("LBL_LEVEL", ""); - setControlText("LBL_LEVEL_VAL", ""); + setControlText("LBL_LEVEL_VAL", "1"); loadClassSelection(); loadQuickOrCustom(); @@ -232,9 +231,28 @@ const CreatureConfiguration &CharacterGeneration::character() const { void CharacterGeneration::setCharacter(const CreatureConfiguration &config) { _character = config; loadCharacterModel(); + updateAttributes(); _portraitSelection->updatePortraits(); } +void CharacterGeneration::updateAttributes() { + setControlText("LBL_CLASS", getClassTitle(_character.clazz)); + + CreatureAttributes attrs(getClassAttributes(_character.clazz)); + int vitality = getClassHitPoints(_character.clazz, 1) + (attrs.constitution() - 10) / 2; + int defense = 10 + getClassDefenseBonus(_character.clazz, 1) + (attrs.dexterity() - 10) / 2; + + setControlText("LBL_VIT", to_string(vitality)); + setControlText("LBL_DEF", to_string(defense)); + + setControlText("STR_AB_LBL", to_string(attrs.strength())); + setControlText("DEX_AB_LBL", to_string(attrs.dexterity())); + setControlText("CON_AB_LBL", to_string(attrs.constitution())); + setControlText("INT_AB_LBL", to_string(attrs.intelligence())); + setControlText("WIS_AB_LBL", to_string(attrs.wisdom())); + setControlText("CHA_AB_LBL", to_string(attrs.charisma())); +} + void CharacterGeneration::setQuickStep(int step) { _quick->setStep(step); } diff --git a/src/game/gui/chargen/chargen.h b/src/game/gui/chargen/chargen.h index 652fcb59..457e8dc4 100644 --- a/src/game/gui/chargen/chargen.h +++ b/src/game/gui/chargen/chargen.h @@ -83,6 +83,7 @@ private: // END Sub GUI void loadCharacterModel(); + void updateAttributes(); gui::GUI *getSubGUI() const; std::shared_ptr getCharacterModel(scene::SceneGraph &sceneGraph); diff --git a/src/game/gui/chargen/classselect.cpp b/src/game/gui/chargen/classselect.cpp index 724eba7f..c6d13449 100644 --- a/src/game/gui/chargen/classselect.cpp +++ b/src/game/gui/chargen/classselect.cpp @@ -23,6 +23,7 @@ #include "../../characters.h" #include "../../game.h" #include "../../object/creature.h" +#include "../../rp/classes.h" #include "../colors.h" @@ -47,15 +48,6 @@ static map g_genderStrRefs { { Gender::Female, 647 } }; -static map g_classStrRefs { - { ClassType::Scout, 133 }, - { ClassType::Soldier, 134 }, - { ClassType::Scoundrel, 135 }, - { ClassType::JediGuardian, 353 }, - { ClassType::JediConsular, 354 }, - { ClassType::JediSentinel, 355 } -}; - static map g_classDescStrRefs { { ClassType::Scoundrel, 32109 }, { ClassType::Scout, 32110 }, @@ -228,7 +220,7 @@ void ClassSelection::onFocusChanged(const string &control, bool focus) { ClassButton &button = _classButtons[idx]; string classText(Resources::instance().getString(g_genderStrRefs[button.config.gender])); - classText += " " + Resources::instance().getString(g_classStrRefs[button.config.clazz]); + classText += " " + getClassTitle(button.config.clazz); string descText(Resources::instance().getString(g_classDescStrRefs[button.config.clazz])); diff --git a/src/game/object/creature.cpp b/src/game/object/creature.cpp index 7f1b67a9..bc789cb9 100644 --- a/src/game/object/creature.cpp +++ b/src/game/object/creature.cpp @@ -93,6 +93,10 @@ void Creature::load(const shared_ptr &blueprint) { } shared_ptr appearance(Resources::instance().get2DA("appearance")); loadAppearance(*appearance, _blueprint->appearance()); + + _attributes = blueprint->attributes(); + _onSpawn = blueprint->onSpawn(); + _onUserDefined = blueprint->onUserDefined(); } void Creature::loadAppearance(const TwoDaTable &table, int row) { diff --git a/src/game/object/creature.h b/src/game/object/creature.h index db1d9a27..5ffefa77 100644 --- a/src/game/object/creature.h +++ b/src/game/object/creature.h @@ -116,6 +116,13 @@ private: float _runSpeed { 0.0f }; MovementType _movementType { MovementType::None }; bool _talking { false }; + CreatureAttributes _attributes; + + // Scripts + + std::string _onSpawn; + + // END Scripts // Loading diff --git a/src/game/rp/classes.cpp b/src/game/rp/classes.cpp new file mode 100644 index 00000000..7cc70ed3 --- /dev/null +++ b/src/game/rp/classes.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2020 Vsevolod Kremianskii + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "classes.h" + +#include + +#include "../../resource/resources.h" + +using namespace std; + +using namespace reone::resource; + +namespace reone { + +namespace game { + +static map g_classStrRefs { + { ClassType::Scout, 133 }, + { ClassType::Soldier, 134 }, + { ClassType::Scoundrel, 135 }, + { ClassType::JediGuardian, 353 }, + { ClassType::JediConsular, 354 }, + { ClassType::JediSentinel, 355 } +}; + +const string &getClassTitle(ClassType clazz) { + int strRef = g_classStrRefs.find(clazz)->second; + return Resources::instance().getString(strRef); +} + +CreatureAttributes getClassAttributes(ClassType clazz) { + shared_ptr classes(Resources::instance().get2DA("classes")); + int row = static_cast(clazz); + + CreatureAttributes attrs; + attrs.abilities.insert(make_pair(Ability::Strength, classes->getInt(row, "str"))); + attrs.abilities.insert(make_pair(Ability::Dexterity, classes->getInt(row, "dex"))); + attrs.abilities.insert(make_pair(Ability::Constitution, classes->getInt(row, "con"))); + attrs.abilities.insert(make_pair(Ability::Intelligence, classes->getInt(row, "int"))); + attrs.abilities.insert(make_pair(Ability::Wisdom, classes->getInt(row, "wis"))); + attrs.abilities.insert(make_pair(Ability::Charisma, classes->getInt(row, "cha"))); + + return move(attrs); +} + +int getClassHitPoints(ClassType clazz, int level) { + shared_ptr classes(Resources::instance().get2DA("classes")); + int row = static_cast(clazz); + + return level * classes->getInt(row, "hitdie"); +} + +int getClassDefenseBonus(ClassType clazz, int level) { + switch (clazz) { + case ClassType::JediConsular: + case ClassType::JediGuardian: + case ClassType::JediSentinel: + case ClassType::Scoundrel: + return 2 + (2 * (level / 6)); + default: + return 0; + } +} + +} // namespace game + +} // namespace reone diff --git a/src/game/rp/classes.h b/src/game/rp/classes.h new file mode 100644 index 00000000..ab7beb41 --- /dev/null +++ b/src/game/rp/classes.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020 Vsevolod Kremianskii + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +#include "types.h" + +namespace reone { + +namespace game { + +const std::string &getClassTitle(ClassType clazz); +CreatureAttributes getClassAttributes(ClassType clazz); +int getClassHitPoints(ClassType clazz, int level); +int getClassDefenseBonus(ClassType clazz, int level); + +} // namespace game + +} // namespace reone diff --git a/src/game/rp/types.h b/src/game/rp/types.h index 34aaa43b..ea571dfd 100644 --- a/src/game/rp/types.h +++ b/src/game/rp/types.h @@ -17,6 +17,7 @@ #pragma once +#include #include #include @@ -74,8 +75,15 @@ enum class Skill { struct CreatureAttributes { std::vector> classLevels; - std::vector abilities; - std::vector skills; + std::map abilities; + std::map skills; + + int strength() const { return abilities.find(Ability::Strength)->second; } + int dexterity() const { return abilities.find(Ability::Dexterity)->second; } + int constitution() const { return abilities.find(Ability::Constitution)->second; } + int intelligence() const { return abilities.find(Ability::Intelligence)->second; } + int wisdom() const { return abilities.find(Ability::Wisdom)->second; } + int charisma() const { return abilities.find(Ability::Charisma)->second; } }; } // namespace game