You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
499 lines
14 KiB
499 lines
14 KiB
/** |
|
* @file player.h |
|
* |
|
* Interface of player functionality, leveling, actions, creation, loading, etc. |
|
*/ |
|
#pragma once |
|
|
|
#include <stdint.h> |
|
|
|
#include "diablo.h" |
|
#include "engine.h" |
|
#include "gendung.h" |
|
#include "items.h" |
|
#include "multi.h" |
|
#include "spelldat.h" |
|
#include "path.h" |
|
#include "interfac.h" |
|
#include "utils/enum_traits.h" |
|
|
|
namespace devilution { |
|
|
|
// number of inventory grid cells |
|
#define NUM_INV_GRID_ELEM 40 |
|
#define MAXBELTITEMS 8 |
|
#define MAXRESIST 75 |
|
#define MAXCHARLEVEL 51 |
|
#define MAX_SPELL_LEVEL 15 |
|
#define PLR_NAME_LEN 32 |
|
|
|
/** Walking directions */ |
|
enum { |
|
// clang-format off |
|
WALK_NE = 1, |
|
WALK_NW = 2, |
|
WALK_SE = 3, |
|
WALK_SW = 4, |
|
WALK_N = 5, |
|
WALK_E = 6, |
|
WALK_S = 7, |
|
WALK_W = 8, |
|
WALK_NONE = -1, |
|
// clang-format on |
|
}; |
|
|
|
enum class HeroClass : uint8_t { |
|
Warrior, |
|
Rogue, |
|
Sorcerer, |
|
Monk, |
|
Bard, |
|
Barbarian, |
|
|
|
LAST = Barbarian |
|
}; |
|
|
|
enum class CharacterAttribute : uint8_t { |
|
Strength, |
|
Magic, |
|
Dexterity, |
|
Vitality, |
|
|
|
FIRST = Strength, |
|
LAST = Vitality |
|
}; |
|
|
|
// Logical equipment locations |
|
enum inv_body_loc : uint8_t { |
|
INVLOC_HEAD, |
|
INVLOC_RING_LEFT, |
|
INVLOC_RING_RIGHT, |
|
INVLOC_AMULET, |
|
INVLOC_HAND_LEFT, |
|
INVLOC_HAND_RIGHT, |
|
INVLOC_CHEST, |
|
NUM_INVLOC, |
|
}; |
|
|
|
enum player_graphic : uint16_t { |
|
// clang-format off |
|
PFILE_STAND = 1 << 0, |
|
PFILE_WALK = 1 << 1, |
|
PFILE_ATTACK = 1 << 2, |
|
PFILE_HIT = 1 << 3, |
|
PFILE_LIGHTNING = 1 << 4, |
|
PFILE_FIRE = 1 << 5, |
|
PFILE_MAGIC = 1 << 6, |
|
PFILE_DEATH = 1 << 7, |
|
PFILE_BLOCK = 1 << 8, |
|
// everything except PFILE_DEATH |
|
// 0b1_0111_1111 |
|
PFILE_NONDEATH = 0x17F |
|
// clang-format on |
|
}; |
|
|
|
enum anim_weapon_id : uint8_t { |
|
ANIM_ID_UNARMED, |
|
ANIM_ID_UNARMED_SHIELD, |
|
ANIM_ID_SWORD, |
|
ANIM_ID_SWORD_SHIELD, |
|
ANIM_ID_BOW, |
|
ANIM_ID_AXE, |
|
ANIM_ID_MACE, |
|
ANIM_ID_MACE_SHIELD, |
|
ANIM_ID_STAFF, |
|
}; |
|
|
|
enum PLR_MODE : uint8_t { |
|
PM_STAND, |
|
PM_WALK, //Movement towards N, NW, or NE |
|
PM_WALK2, //Movement towards S, SW, or SE |
|
PM_WALK3, //Movement towards W or E |
|
PM_ATTACK, |
|
PM_RATTACK, |
|
PM_BLOCK, |
|
PM_GOTHIT, |
|
PM_DEATH, |
|
PM_SPELL, |
|
PM_NEWLVL, |
|
PM_QUIT, |
|
}; |
|
|
|
enum action_id : int8_t { |
|
// clang-format off |
|
ACTION_WALK = -2, // Automatic walk when using gamepad |
|
ACTION_NONE = -1, |
|
ACTION_ATTACK = 9, |
|
ACTION_RATTACK = 10, |
|
ACTION_SPELL = 12, |
|
ACTION_OPERATE = 13, |
|
ACTION_DISARM = 14, |
|
ACTION_PICKUPITEM = 15, // put item in hand (inventory screen open) |
|
ACTION_PICKUPAITEM = 16, // put item in inventory |
|
ACTION_TALK = 17, |
|
ACTION_OPERATETK = 18, // operate via telekinesis |
|
ACTION_ATTACKMON = 20, |
|
ACTION_ATTACKPLR = 21, |
|
ACTION_RATTACKMON = 22, |
|
ACTION_RATTACKPLR = 23, |
|
ACTION_SPELLMON = 24, |
|
ACTION_SPELLPLR = 25, |
|
ACTION_SPELLWALL = 26, |
|
// clang-format on |
|
}; |
|
|
|
enum player_weapon_type : uint8_t { |
|
WT_MELEE, |
|
WT_RANGED, |
|
}; |
|
|
|
struct PlayerStruct { |
|
PLR_MODE _pmode; |
|
int8_t walkpath[MAX_PATH_LENGTH]; |
|
bool plractive; |
|
action_id destAction; |
|
int destParam1; |
|
int destParam2; |
|
direction destParam3; |
|
int destParam4; |
|
int plrlevel; |
|
ActorPosition position; |
|
direction _pdir; // Direction faced by player (direction enum) |
|
int _pgfxnum; // Bitmask indicating what variant of the sprite the player is using. Lower byte define weapon (anim_weapon_id) and higher values define armour (starting with anim_armor_id) |
|
uint8_t *_pAnimData; |
|
int _pAnimDelay; // Tick length of each frame in the current animation |
|
int _pAnimCnt; // Increases by one each game tick, counting how close we are to _pAnimDelay |
|
int _pAnimLen; // Number of frames in current animation |
|
int _pAnimFrame; // Current frame of animation. |
|
int _pAnimWidth; |
|
int _pAnimWidth2; |
|
/* |
|
* @brief Specifies how many animations-fractions are displayed between two gameticks. this can be > 0, if animations are skipped or < 0 if the same animation is shown in multiple times (delay specified). |
|
*/ |
|
float _pAnimGameTickModifier; |
|
/* |
|
* @brief Number of GameTicks after the current animation sequence started |
|
*/ |
|
int _pAnimGameTicksSinceSequenceStarted; |
|
/* |
|
* @brief Animation Frames that will be adjusted for the skipped Frames/GameTicks |
|
*/ |
|
int _pAnimRelevantAnimationFramesForDistributing; |
|
int _plid; |
|
int _pvid; |
|
spell_id _pSpell; |
|
spell_type _pSplType; |
|
int8_t _pSplFrom; // TODO Create enum |
|
spell_id _pTSpell; |
|
spell_type _pTSplType; |
|
spell_id _pRSpell; |
|
spell_type _pRSplType; |
|
spell_id _pSBkSpell; |
|
spell_type _pSBkSplType; |
|
int8_t _pSplLvl[64]; |
|
uint64_t _pMemSpells; // Bitmask of learned spells |
|
uint64_t _pAblSpells; // Bitmask of abilities |
|
uint64_t _pScrlSpells; // Bitmask of spells available via scrolls |
|
uint8_t _pSpellFlags; |
|
spell_id _pSplHotKey[4]; |
|
spell_type _pSplTHotKey[4]; |
|
player_weapon_type _pwtype; |
|
bool _pBlockFlag; |
|
bool _pInvincible; |
|
int8_t _pLightRad; |
|
bool _pLvlChanging; // True when the player is transitioning between levels |
|
char _pName[PLR_NAME_LEN]; |
|
HeroClass _pClass; |
|
int _pStrength; |
|
int _pBaseStr; |
|
int _pMagic; |
|
int _pBaseMag; |
|
int _pDexterity; |
|
int _pBaseDex; |
|
int _pVitality; |
|
int _pBaseVit; |
|
int _pStatPts; |
|
int _pDamageMod; |
|
int _pBaseToBlk; |
|
int _pHPBase; |
|
int _pMaxHPBase; |
|
int _pHitPoints; |
|
int _pMaxHP; |
|
int _pHPPer; |
|
int _pManaBase; |
|
int _pMaxManaBase; |
|
int _pMana; |
|
int _pMaxMana; |
|
int _pManaPer; |
|
int8_t _pLevel; |
|
int8_t _pMaxLvl; |
|
int _pExperience; |
|
int _pMaxExp; |
|
int _pNextExper; |
|
int8_t _pArmorClass; |
|
int8_t _pMagResist; |
|
int8_t _pFireResist; |
|
int8_t _pLghtResist; |
|
int _pGold; |
|
bool _pInfraFlag; |
|
/** Player's direction when ending movement. Also used for casting direction of SPL_FIREWALL. */ |
|
direction tempDirection; |
|
/** Used for spell level, and X component of _pVar5 */ |
|
int _pVar4; |
|
/** Used for storing position of a tile which should have its BFLAG_PLAYERLR flag removed after walking. When starting to walk the game places the player in the dPlayer array -1 in the Y coordinate, and uses BFLAG_PLAYERLR to check if it should be using -1 to the Y coordinate when rendering the player (also used for storing the level of a spell when the player casts it) */ |
|
int _pVar5; |
|
/** Used for counting how close we are to reaching the next tile when walking (usually counts to 8, which is equal to the walk animation length). */ |
|
int actionFrame; |
|
/** Used for stalling the appearance of the options screen after dying in singleplayer */ |
|
int deathFrame; |
|
bool _pLvlVisited[NUMLEVELS]; |
|
bool _pSLvlVisited[NUMLEVELS]; // only 10 used |
|
/** Using player_graphic as bitflags */ |
|
int _pGFXLoad; |
|
uint8_t *_pNAnim[8]; // Stand animations |
|
int _pNFrames; |
|
int _pNWidth; |
|
uint8_t *_pWAnim[8]; // Walk animations |
|
int _pWFrames; |
|
int _pWWidth; |
|
uint8_t *_pAAnim[8]; // Attack animations |
|
int _pAFrames; |
|
int _pAWidth; |
|
int _pAFNum; |
|
uint8_t *_pLAnim[8]; // Lightning spell cast animations |
|
uint8_t *_pFAnim[8]; // Fire spell cast animations |
|
uint8_t *_pTAnim[8]; // Generic spell cast animations |
|
int _pSFrames; |
|
int _pSWidth; |
|
int _pSFNum; |
|
uint8_t *_pHAnim[8]; // Getting hit animations |
|
int _pHFrames; |
|
int _pHWidth; |
|
uint8_t *_pDAnim[8]; // Death animations |
|
int _pDFrames; |
|
int _pDWidth; |
|
uint8_t *_pBAnim[8]; // Block animations |
|
int _pBFrames; |
|
int _pBWidth; |
|
ItemStruct InvBody[NUM_INVLOC]; |
|
ItemStruct InvList[NUM_INV_GRID_ELEM]; |
|
int _pNumInv; |
|
int8_t InvGrid[NUM_INV_GRID_ELEM]; |
|
ItemStruct SpdList[MAXBELTITEMS]; |
|
ItemStruct HoldItem; |
|
int _pIMinDam; |
|
int _pIMaxDam; |
|
int _pIAC; |
|
int _pIBonusDam; |
|
int _pIBonusToHit; |
|
int _pIBonusAC; |
|
int _pIBonusDamMod; |
|
/** Bitmask of staff spell */ |
|
uint64_t _pISpells; |
|
/** Bitmask using item_special_effect */ |
|
int _pIFlags; |
|
int _pIGetHit; |
|
int8_t _pISplLvlAdd; |
|
int _pISplDur; |
|
int _pIEnAc; |
|
int _pIFMinDam; |
|
int _pIFMaxDam; |
|
int _pILMinDam; |
|
int _pILMaxDam; |
|
item_misc_id _pOilType; |
|
uint8_t pTownWarps; |
|
uint8_t pDungMsgs; |
|
uint8_t pLvlLoad; |
|
bool pBattleNet; |
|
bool pManaShield; |
|
uint8_t pDungMsgs2; |
|
bool pOriginalCathedral; |
|
uint16_t wReflections; |
|
uint8_t pDiabloKillLevel; |
|
_difficulty pDifficulty; |
|
uint32_t pDamAcFlags; |
|
uint8_t *_pNData; |
|
uint8_t *_pWData; |
|
uint8_t *_pAData; |
|
uint8_t *_pLData; |
|
uint8_t *_pFData; |
|
uint8_t *_pTData; |
|
uint8_t *_pHData; |
|
uint8_t *_pDData; |
|
uint8_t *_pBData; |
|
|
|
/** |
|
* @brief Gets the most valuable item out of all the player's items that match the given predicate. |
|
* @param itemPredicate The predicate used to match the items. |
|
* @return The most valuable item out of all the player's items that match the given predicate, or 'nullptr' in case no |
|
* matching items were found. |
|
*/ |
|
template <typename TPredicate> |
|
const ItemStruct *GetMostValuableItem(const TPredicate &itemPredicate) const |
|
{ |
|
const auto getMostValuableItem = [&itemPredicate](const ItemStruct *begin, const ItemStruct *end, const ItemStruct *mostValuableItem = nullptr) { |
|
for (const auto *item = begin; item < end; item++) { |
|
if (item->isEmpty() || !itemPredicate(*item)) { |
|
continue; |
|
} |
|
|
|
if (mostValuableItem == nullptr || item->_iIvalue > mostValuableItem->_iIvalue) { |
|
mostValuableItem = item; |
|
} |
|
} |
|
|
|
return mostValuableItem; |
|
}; |
|
|
|
const ItemStruct *mostValuableItem = getMostValuableItem(SpdList, SpdList + MAXBELTITEMS); |
|
mostValuableItem = getMostValuableItem(InvBody, InvBody + inv_body_loc::NUM_INVLOC, mostValuableItem); |
|
mostValuableItem = getMostValuableItem(InvList, InvList + _pNumInv, mostValuableItem); |
|
|
|
return mostValuableItem; |
|
} |
|
|
|
/** |
|
* @brief Gets the base value of the player's specified attribute. |
|
* @param attribute The attribute to retrieve the base value for |
|
* @return The base value for the requested attribute. |
|
*/ |
|
int GetBaseAttributeValue(CharacterAttribute attribute) const; |
|
|
|
/** |
|
* @brief Gets the maximum value of the player's specified attribute. |
|
* @param attribute The attribute to retrieve the maximum value for |
|
* @return The maximum value for the requested attribute. |
|
*/ |
|
int GetMaximumAttributeValue(CharacterAttribute attribute) const; |
|
|
|
/** |
|
* @brief Get the tile coordinates a player is moving to (if not moving, then it corresponds to current position). |
|
*/ |
|
Point GetTargetPosition() const; |
|
|
|
/** |
|
* @brief Play a speach file. |
|
* @todo Create enum for speachId |
|
* @todo BUGFIX Prevent more then one speach to be played at a time (reject new requests). |
|
*/ |
|
void PlaySpeach(int speachId) const; |
|
/** |
|
* @brief Play a speach file after a given delay. |
|
* @param delay Multiple of 20ms waith before starting the speach |
|
*/ |
|
void PlaySpeach(int speachId, int delay) const; |
|
/** |
|
* @brief Play a player speach file, with out random variants. |
|
*/ |
|
void PlaySpecificSpeach(int speachId) const; |
|
}; |
|
|
|
extern int myplr; |
|
extern PlayerStruct plr[MAX_PLRS]; |
|
extern bool deathflag; |
|
extern int ToBlkTbl[enum_size<HeroClass>::value]; |
|
|
|
void LoadPlrGFX(int pnum, player_graphic gfxflag); |
|
void InitPlayerGFX(int pnum); |
|
void InitPlrGFXMem(int pnum); |
|
void FreePlayerGFX(int pnum); |
|
|
|
/** |
|
* @brief Specifies what special logics are applied for a Animation |
|
*/ |
|
enum class AnimationDistributionParams : uint8_t { |
|
None = 0, |
|
/* |
|
* @brief ProcessAnimation will be called after NewPlrAnim (in same GameTick as NewPlrAnim) |
|
*/ |
|
ProcessAnimationPending, |
|
/* |
|
* @brief Delay of last Frame is ignored (for example, because only Frame and not delay is checked in game_logic) |
|
*/ |
|
SkipsDelayOfLastFrame, |
|
}; |
|
|
|
/** |
|
* @brief Sets the new Player Animation with all relevant information for rendering |
|
|
|
* @param pnum Player Id |
|
* @param Peq Pointer to Animation Data |
|
* @param numFrames Number of Frames in Animation |
|
* @param Delay Delay after each Animation sequence |
|
* @param width Width of sprite |
|
* @param params Specifies what special logics are applied to this Animation |
|
* @param numSkippedFrames Number of Frames that will be skipped (for example with modifier "faster attack") |
|
* @param distributeFramesBeforeFrame Distribute the numSkippedFrames only before this frame |
|
*/ |
|
void NewPlrAnim(int pnum, BYTE *Peq, int numFrames, int Delay, int width, AnimationDistributionParams params = AnimationDistributionParams::None, int numSkippedFrames = 0, int distributeFramesBeforeFrame = 0); |
|
void SetPlrAnims(int pnum); |
|
void ProcessPlayerAnimation(int pnum); |
|
/** |
|
* @brief Calculates the Frame to use for the Animation rendering |
|
* @param pPlayer Player |
|
* @return The Frame to use for rendering |
|
*/ |
|
int GetFrameToUseForPlayerRendering(const PlayerStruct *pPlayer); |
|
void CreatePlayer(int pnum, HeroClass c); |
|
int CalcStatDiff(int pnum); |
|
#ifdef _DEBUG |
|
void NextPlrLevel(int pnum); |
|
#endif |
|
void AddPlrExperience(int pnum, int lvl, int exp); |
|
void AddPlrMonstExper(int lvl, int exp, char pmask); |
|
void ApplyPlrDamage(int pnum, int dam, int minHP = 0, int frac = 0, int earflag = 0); |
|
void InitPlayer(int pnum, bool FirstTime); |
|
void InitMultiView(); |
|
bool SolidLoc(int x, int y); |
|
void PlrClrTrans(int x, int y); |
|
void PlrDoTrans(int x, int y); |
|
void SetPlayerOld(int pnum); |
|
void FixPlayerLocation(int pnum, direction bDir); |
|
void StartStand(int pnum, direction dir); |
|
void StartAttack(int pnum, direction d); |
|
void StartPlrBlock(int pnum, direction dir); |
|
void FixPlrWalkTags(int pnum); |
|
void RemovePlrFromMap(int pnum); |
|
void StartPlrHit(int pnum, int dam, bool forcehit); |
|
void StartPlayerKill(int pnum, int earflag); |
|
void DropHalfPlayersGold(int pnum); |
|
void StripTopGold(int pnum); |
|
void SyncPlrKill(int pnum, int earflag); |
|
void RemovePlrMissiles(int pnum); |
|
void StartNewLvl(int pnum, interface_mode fom, int lvl); |
|
void RestartTownLvl(int pnum); |
|
void StartWarpLvl(int pnum, int pidx); |
|
void ProcessPlayers(); |
|
void ClrPlrPath(int pnum); |
|
bool PosOkPlayer(int pnum, int x, int y); |
|
void MakePlrPath(int pnum, int xx, int yy, bool endspace); |
|
void CheckPlrSpell(); |
|
void SyncPlrAnim(int pnum); |
|
void SyncInitPlrPos(int pnum); |
|
void SyncInitPlr(int pnum); |
|
void CheckStats(int p); |
|
void ModifyPlrStr(int p, int l); |
|
void ModifyPlrMag(int p, int l); |
|
void ModifyPlrDex(int p, int l); |
|
void ModifyPlrVit(int p, int l); |
|
void SetPlayerHitPoints(int pnum, int val); |
|
void SetPlrStr(int p, int v); |
|
void SetPlrMag(int p, int v); |
|
void SetPlrDex(int p, int v); |
|
void SetPlrVit(int p, int v); |
|
void InitDungMsgs(int pnum); |
|
void PlayDungMsgs(); |
|
|
|
/* data */ |
|
|
|
extern int plrxoff[9]; |
|
extern int plryoff[9]; |
|
extern int plrxoff2[9]; |
|
extern int plryoff2[9]; |
|
extern int StrengthTbl[enum_size<HeroClass>::value]; |
|
extern int MagicTbl[enum_size<HeroClass>::value]; |
|
extern int DexterityTbl[enum_size<HeroClass>::value]; |
|
extern int VitalityTbl[enum_size<HeroClass>::value]; |
|
extern int ExpLvlsTbl[MAXCHARLEVEL]; |
|
|
|
} // namespace devilution
|
|
|