Skip to content

Commit

Permalink
Merge pull request #497 from wheybags/magic_items
Browse files Browse the repository at this point in the history
Magic items
  • Loading branch information
wheybags authored Jun 12, 2020
2 parents 4ef76c1 + 8f07f52 commit 921ac20
Show file tree
Hide file tree
Showing 56 changed files with 1,419 additions and 706 deletions.
4 changes: 1 addition & 3 deletions apps/freeablo/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ add_library(freeablo_lib # split into a library so I can link to it from tests
faworld/hoverstate.h
faworld/inventory.cpp
faworld/inventory.h
faworld/itemenums.cpp
faworld/itemenums.h
faworld/itemfactory.cpp
faworld/itemfactory.h
faworld/itemmap.cpp
Expand Down Expand Up @@ -176,7 +174,7 @@ add_library(freeablo_lib # split into a library so I can link to it from tests
fasavegame/objectidmapper.h
fasavegame/objectidmapper.cpp
fasavegame/gameloader.h
fasavegame/gameloader.cpp farender/levelrenderer.cpp farender/levelrenderer.h engine/debugsettings.h engine/debugsettings.cpp faworld/item/golditembase.cpp faworld/item/golditembase.h faworld/item/golditem.cpp faworld/item/golditem.h)
fasavegame/gameloader.cpp farender/levelrenderer.cpp farender/levelrenderer.h engine/debugsettings.h engine/debugsettings.cpp faworld/item/golditembase.cpp faworld/item/golditembase.h faworld/item/golditem.cpp faworld/item/golditem.h faworld/magiceffects/magiceffectbase.cpp faworld/magiceffects/magiceffectbase.h faworld/item/itemprefixorsuffixbase.cpp faworld/item/itemprefixorsuffixbase.h faworld/magiceffects/magiceffect.cpp faworld/magiceffects/magiceffect.h faworld/magiceffects/simplebuffdebuffeffect.cpp faworld/magiceffects/simplebuffdebuffeffect.h faworld/magiceffects/simplebuffdebuffeffectbase.cpp faworld/magiceffects/simplebuffdebuffeffectbase.h faworld/item/itemprefixorsuffix.cpp faworld/item/itemprefixorsuffix.h)

target_link_libraries(freeablo_lib PUBLIC NuklearMisc Render Audio Serial Input Random Image enet cxxopts fmt::fmt)
target_include_directories(freeablo_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
Expand Down
5 changes: 5 additions & 0 deletions apps/freeablo/engine/debugsettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,9 @@ namespace DebugSettings
{
bool DebugMissiles = false;
bool DebugLevelTransitions = false;
bool Instakill = false;
bool EnemiesFrozen = false;
bool PlayersInvuln = false;
bool DisableMusic = false;
ItemGenerationType itemGenerationType = ItemGenerationType::Normal;
}
12 changes: 12 additions & 0 deletions apps/freeablo/engine/debugsettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,16 @@ namespace DebugSettings
{
extern bool DebugMissiles;
extern bool DebugLevelTransitions;
extern bool Instakill;
extern bool EnemiesFrozen;
extern bool PlayersInvuln;
extern bool DisableMusic;

enum class ItemGenerationType
{
Normal,
AlwaysMagical,
};

extern ItemGenerationType itemGenerationType;
}
2 changes: 0 additions & 2 deletions apps/freeablo/engine/enginemain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,6 @@ namespace Engine
mMultiplayer = std::make_unique<Server>(*mWorld, *mLocalInputHandler);

player = mPlayerFactory->create(*mWorld, characterClass);
if (variables["invuln"].as<std::string>() == "on")
player->mInvuln = true;
}
}
else
Expand Down
4 changes: 4 additions & 0 deletions apps/freeablo/engine/threadmanager.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "threadmanager.h"
#include "../farender/renderer.h"
#include "debugsettings.h"
#include <chrono>
#include <input/inputmanager.h>
#include <iostream>
Expand Down Expand Up @@ -53,6 +54,9 @@ namespace Engine

void ThreadManager::playMusic(const std::string& path)
{
if (DebugSettings::DisableMusic)
return;

Message message = {};
message.type = ThreadState::PLAY_MUSIC;
message.data.musicPath = new std::string(path);
Expand Down
1 change: 0 additions & 1 deletion apps/freeablo/fa_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ bool parseOptions(int argc, char** argv, cxxopts::ParseResult& variables)
// -1 represents the main menu
("l,level", "Level number to load (0-16)", cxxopts::value<int32_t>()->default_value("-1"))(
"c,character", "Choose Warrior, Rogue or Sorceror", cxxopts::value<std::string>()->default_value("Warrior"))(
"invuln", "on or off", cxxopts::value<std::string>()->default_value("off"))(
"connect", "Ip Address or hostname to connect to", cxxopts::value<std::string>()->default_value(""))(
"seed", "Seed for level generation", cxxopts::value<uint32_t>()->default_value("0"));

Expand Down
1 change: 0 additions & 1 deletion apps/freeablo/fagui/guimanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#include "../fasavegame/gameloader.h"
#include "../faworld/actorstats.h"
#include "../faworld/equiptarget.h"
#include "../faworld/itemenums.h"
#include "../faworld/player.h"
#include "../faworld/playerbehaviour.h"
#include "../faworld/spells.h"
Expand Down
9 changes: 8 additions & 1 deletion apps/freeablo/faworld/actor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "world.h"
#include <diabloexe/monster.h>
#include <diabloexe/npc.h>
#include <engine/debugsettings.h>
#include <fmt/format.h>

namespace FAWorld
Expand Down Expand Up @@ -181,6 +182,12 @@ namespace FAWorld
if (mInvuln)
return;

if (DebugSettings::Instakill)
{
die();
return;
}

// https://wheybags.gitlab.io/jarulfs-guide/#getting-hit
int32_t blockChance = getStats().getCalculatedStats().blockChance;
blockChance += 2 * (getStats().mLevel - attacker->getStats().mLevel);
Expand Down Expand Up @@ -371,7 +378,7 @@ namespace FAWorld
printf("%s melee attacks %s - ", mName.c_str(), enemy->mName.c_str());
#endif

if (roll < toHit)
if (roll < toHit || DebugSettings::Instakill)
{
int32_t damage = stats.meleeDamage;
damage += mWorld.mRng->randomInRange(stats.meleeDamageBonusRange.start, stats.meleeDamageBonusRange.end);
Expand Down
9 changes: 8 additions & 1 deletion apps/freeablo/faworld/actorstats.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,20 @@ namespace FAWorld
}
};

struct ItemStats
struct MagicStatModifiers
{
BaseStats baseStats;
int32_t maxLife = 0;
int32_t maxMana = 0;
int32_t armorClass = 0;
int32_t toHit = 0;
int32_t meleeDamageBonus = 0;
int32_t rangedDamageBonus = 0;
};

struct ItemStats
{
MagicStatModifiers magicStatModifiers;
IntRange meleeDamageBonusRange = {0, 0};
IntRange rangedDamageBonusRange = {0, 0};
};
Expand Down
3 changes: 2 additions & 1 deletion apps/freeablo/faworld/behaviour.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "actor.h"
#include "player.h"
#include <cstdlib>
#include <engine/debugsettings.h>
#include <iostream>
#include <misc/assert.h>
#include <random/random.h>
Expand Down Expand Up @@ -51,7 +52,7 @@ namespace FAWorld

void BasicMonsterBehaviour::update()
{
if (mActor->mTarget.getType() != Target::Type::None)
if (mActor->mTarget.getType() != Target::Type::None || DebugSettings::EnemiesFrozen)
return;

mTicksSinceLastAction++;
Expand Down
7 changes: 7 additions & 0 deletions apps/freeablo/faworld/enums.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@

namespace FAWorld
{
enum class Difficulty
{
Normal,
Nightmare,
Hell,
};

enum class ActorType : uint8_t
{
// Undead, Demon and Animal set up to match the values from Diablo
Expand Down
4 changes: 2 additions & 2 deletions apps/freeablo/faworld/gamelevel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,15 +169,15 @@ namespace FAWorld
Actor* blocking = nullptr;
if (mActorMap2D.count(actor->getPos().current()))
blocking = mActorMap2D[actor->getPos().current()];
debug_assert(blocking == actor || blocking == nullptr || blocking->isDead());
debug_assert(blocking == actor || blocking == nullptr || (blocking->getWorld()->mLoading || blocking->isDead()));

mActorMap2D[actor->getPos().current()] = actor;

if (actor->getPos().isMoving())
{
if (mActorMap2D.count(actor->getPos().next()))
blocking = mActorMap2D[actor->getPos().next()];
debug_assert(blocking == actor || blocking == nullptr || blocking->isDead());
debug_assert(blocking == actor || blocking == nullptr || (blocking->getWorld()->mLoading || blocking->isDead()));

mActorMap2D[actor->getPos().next()] = actor;
}
Expand Down
40 changes: 33 additions & 7 deletions apps/freeablo/faworld/inventory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,20 @@
#include "../fagui/guimanager.h"
#include "../fasavegame/gameloader.h"
#include "../faworld/actorstats.h"
#include "actorstats.h"
#include "equiptarget.h"
#include "item/equipmentitem.h"
#include "item/equipmentitembase.h"
#include "item/itembase.h"
#include "item/itemprefixorsuffix.h"
#include "item/usableitem.h"
#include "item/usableitembase.h"
#include "itemenums.h"
#include "itemfactory.h"
#include "player.h"
#include <algorithm>
#include <cstdint>
#include <engine/enginemain.h>
#include <faworld/item/golditem.h>
#include <faworld/item/golditembase.h>
#include <iostream>
#include <sstream>
#include <string>

namespace FAWorld
Expand Down Expand Up @@ -113,6 +110,8 @@ namespace FAWorld

bool BasicInventory::autoPlaceItem(std::unique_ptr<Item>& item)
{
release_assert(item);

// TODO: the original game had some fancier methods of trying to fit specific size items
// There used to be an implementation of this here, but it was buggy so I removed it,
// as I didn't want to spend time debugging it.
Expand Down Expand Up @@ -300,6 +299,8 @@ namespace FAWorld

bool CharacterInventory::autoPlaceItem(std::unique_ptr<Item>& item)
{
release_assert(item);

// auto-placing in belt
if (item->getAsUsableItem() && item->getAsUsableItem()->getBase()->isBeltEquippable() && mBelt.autoPlaceItem(item))
return true;
Expand Down Expand Up @@ -329,9 +330,14 @@ namespace FAWorld
count = placeGold(count, Engine::EngineMain::get()->mWorld->getItemFactory());

if (count)
{
release_assert(goldItem->trySetCount(count));
}
else
{
item.reset();
return true;
}
}

return mMainInventory.autoPlaceItem(item);
Expand Down Expand Up @@ -486,6 +492,26 @@ namespace FAWorld

// TODO: other stats
}

EquipTarget allEquipmentSlots[] = {MakeEquipTarget<EquipTargetType::head>(),
MakeEquipTarget<EquipTargetType::body>(),
MakeEquipTarget<EquipTargetType::leftRing>(),
MakeEquipTarget<EquipTargetType::rightRing>(),
MakeEquipTarget<EquipTargetType::leftHand>(),
MakeEquipTarget<EquipTargetType::rightHand>(),
MakeEquipTarget<EquipTargetType::amulet>()};

for (const auto& slot : allEquipmentSlots)
{
const EquipmentItem* item = getItemAt(slot) ? getItemAt(slot)->getAsEquipmentItem() : nullptr;
if (item)
{
if (item->mPrefix)
item->mPrefix->apply(stats.magicStatModifiers);
if (item->mSuffix)
item->mSuffix->apply(stats.magicStatModifiers);
}
}
}

bool CharacterInventory::isRangedWeaponEquipped() const { return getItemsInHands().rangedWeapon.has_value(); }
Expand Down Expand Up @@ -554,7 +580,7 @@ namespace FAWorld
{
if (!mMainInventory.getItem(x, y))
{
std::unique_ptr<Item> newItem = itemFactory.generateBaseItem(ItemId::gold);
std::unique_ptr<Item> newItem = itemFactory.generateBaseItem("gold");
GoldItem* goldItem = newItem->getAsGoldItem();

int32_t toPlace = std::min(quantity, goldItem->getBase()->mMaxCount);
Expand Down Expand Up @@ -590,7 +616,7 @@ namespace FAWorld
}

const GoldItemBase* goldItemBase =
safe_downcast<const GoldItemBase*>(Engine::EngineMain::get()->mWorld->getItemFactory().getItemBaseHolder().get("gold"));
safe_downcast<const GoldItemBase*>(Engine::EngineMain::get()->mWorld->getItemFactory().getItemBaseHolder().getItemBase("gold"));

// second part - filling the empty slots with gold
for (int32_t x = 0; x != mMainInventory.width(); x++)
Expand Down Expand Up @@ -652,7 +678,7 @@ namespace FAWorld
mMainInventory.placeItem(goldFromInventory, x, y);
}

std::unique_ptr<Item> cursorGold = itemFactory.generateBaseItem(ItemId::gold);
std::unique_ptr<Item> cursorGold = itemFactory.generateBaseItem("gold");
release_assert(cursorGold->getAsGoldItem()->trySetCount(amountToTransferToCursor));

setCursorHeld(std::move(cursorGold));
Expand Down
63 changes: 58 additions & 5 deletions apps/freeablo/faworld/item/equipmentitem.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#include "equipmentitem.h"
#include "equipmentitembase.h"
#include "itemprefixorsuffix.h"
#include "itemprefixorsuffixbase.h"
#include <fasavegame/gameloader.h>
#include <faworld/itemfactory.h>
#include <fmt/format.h>
#include <misc/misc.h>

Expand All @@ -14,27 +17,77 @@ namespace FAWorld
mArmorClass = getBase()->mArmorClassRange.max;
}

void EquipmentItem::save(FASaveGame::GameSaver& saver) const { saver.save(mArmorClass); }
void EquipmentItem::save(FASaveGame::GameSaver& saver) const
{
super::save(saver);

saver.save(mArmorClass);

saver.save(mPrefix != nullptr);
if (mPrefix != nullptr)
{
Serial::ScopedCategorySaver cat("Prefix", saver);
saver.save(mPrefix->getBase()->mId);
mPrefix->save(saver);
}

saver.save(mSuffix != nullptr);
if (mSuffix != nullptr)
{
Serial::ScopedCategorySaver cat("Suffix", saver);
saver.save(mSuffix->getBase()->mId);
mSuffix->save(saver);
}
}

void EquipmentItem::load(FASaveGame::GameLoader& loader)
{
super::load(loader);

mArmorClass = Misc::clamp(loader.load<int32_t>(), getBase()->mArmorClassRange.min, getBase()->mArmorClassRange.max);

if (loader.load<bool>())
{
std::string prefixId = loader.load<std::string>();
mPrefix = loader.currentlyLoadingWorld->getItemFactory().getItemBaseHolder().getItemPrefixOrSuffixBase(prefixId)->create();
mPrefix->load(loader);
}

if (loader.load<bool>())
{
std::string suffixId = loader.load<std::string>();
mSuffix = loader.currentlyLoadingWorld->getItemFactory().getItemBaseHolder().getItemPrefixOrSuffixBase(suffixId)->create();
mSuffix->load(loader);
}
}

const EquipmentItemBase* EquipmentItem::getBase() const { return safe_downcast<const EquipmentItemBase*>(mBase); }

std::string EquipmentItem::getFullDescription() const
{
std::string description = super::getFullDescription();
std::string description;
if (mPrefix)
description += mPrefix->getBase()->mName + " ";

description += getBase()->mName;

if (mSuffix)
description += " of " + mSuffix->getBase()->mName;

description += "\n";

if (getBase()->mClass == ItemClass::weapon)
description += fmt::format("\ndamage: {} - {}", getBase()->mDamageBonusRange.start, getBase()->mDamageBonusRange.end);
description += fmt::format("damage: {} - {}\n", getBase()->mDamageBonusRange.start, getBase()->mDamageBonusRange.end);
else
description += fmt::format("\narmor: {}", mArmorClass);
description += fmt::format("armor: {}\n", mArmorClass);

if (mPrefix)
description += mPrefix->getFullDescription();
if (mSuffix)
description += mSuffix->getFullDescription();

// TODO: durability
// TODO: charges
// TODO: prefix / suffix
// TODO: requirements

return description;
Expand Down
Loading

0 comments on commit 921ac20

Please sign in to comment.