/**
* @ file objects . cpp
*
* Implementation of object functionality , interaction , spawning , loading , etc .
*/
# include <algorithm>
# include <climits>
# include <cstdint>
# include "automap.h"
# include "control.h"
# include "cursor.h"
# include "drlg_l1.h"
# include "drlg_l4.h"
# include "engine/load_file.hpp"
# include "engine/random.hpp"
# include "error.h"
# include "init.h"
# include "lighting.h"
# include "minitext.h"
# include "missiles.h"
# include "monster.h"
# include "options.h"
# include "setmaps.h"
# include "stores.h"
# include "themes.h"
# include "towners.h"
# include "track.h"
# include "utils/language.h"
# include "utils/log.hpp"
namespace devilution {
ObjectStruct Objects [ MAXOBJECTS ] ;
int AvailableObjects [ MAXOBJECTS ] ;
int ActiveObjects [ MAXOBJECTS ] ;
int ActiveObjectCount ;
bool ApplyObjectLighting ;
bool LoadingMapObjects ;
namespace {
enum shrine_type : uint8_t {
ShrineMysterious ,
ShrineHidden ,
ShrineGloomy ,
ShrineWeird ,
ShrineMagical ,
ShrineStone ,
ShrineReligious ,
ShrineEnchanted ,
ShrineThaumaturgic ,
ShrineFascinating ,
ShrineCryptic ,
ShrineMagicaL2 ,
ShrineEldritch ,
ShrineEerie ,
ShrineDivine ,
ShrineHoly ,
ShrineSacred ,
ShrineSpiritual ,
ShrineSpooky ,
ShrineAbandoned ,
ShrineCreepy ,
ShrineQuiet ,
ShrineSecluded ,
ShrineOrnate ,
ShrineGlimmering ,
ShrineTainted ,
ShrineOily ,
ShrineGlowing ,
ShrineMendicant ,
ShrineSparkling ,
ShrineTown ,
ShrineShimmering ,
ShrineSolar ,
ShrineMurphys ,
NumberOfShrineTypes
} ;
int trapid ;
int trapdir ;
std : : unique_ptr < byte [ ] > pObjCels [ 40 ] ;
object_graphic_id ObjFileList [ 40 ] ;
/** Specifies the number of active objects. */
int leverid ;
int numobjfiles ;
/** Tracks progress through the tome sequence that spawns Na-Krul (see OperateNakrulBook()) */
int NaKrulTomeSequence ;
/** Specifies the X-coordinate delta between barrels. */
int bxadd [ 8 ] = { - 1 , 0 , 1 , - 1 , 1 , - 1 , 0 , 1 } ;
/** Specifies the Y-coordinate delta between barrels. */
int byadd [ 8 ] = { - 1 , - 1 , - 1 , 0 , 0 , 1 , 1 , 1 } ;
/** Maps from shrine_id to shrine name. */
const char * const ShrineNames [ ] = {
// TRANSLATORS: Shrine Name Block
N_ ( " Mysterious " ) ,
N_ ( " Hidden " ) ,
N_ ( " Gloomy " ) ,
N_ ( " Weird " ) ,
N_ ( " Magical " ) ,
N_ ( " Stone " ) ,
N_ ( " Religious " ) ,
N_ ( " Enchanted " ) ,
N_ ( " Thaumaturgic " ) ,
N_ ( " Fascinating " ) ,
N_ ( " Cryptic " ) ,
N_ ( " Magical " ) ,
N_ ( " Eldritch " ) ,
N_ ( " Eerie " ) ,
N_ ( " Divine " ) ,
N_ ( " Holy " ) ,
N_ ( " Sacred " ) ,
N_ ( " Spiritual " ) ,
N_ ( " Spooky " ) ,
N_ ( " Abandoned " ) ,
N_ ( " Creepy " ) ,
N_ ( " Quiet " ) ,
N_ ( " Secluded " ) ,
N_ ( " Ornate " ) ,
N_ ( " Glimmering " ) ,
N_ ( " Tainted " ) ,
N_ ( " Oily " ) ,
N_ ( " Glowing " ) ,
N_ ( " Mendicant's " ) ,
N_ ( " Sparkling " ) ,
N_ ( " Town " ) ,
N_ ( " Shimmering " ) ,
N_ ( " Solar " ) ,
// TRANSLATORS: Shrine Name Block end
N_ ( " Murphy's " ) ,
} ;
/** Specifies the minimum dungeon level on which each shrine will appear. */
char shrinemin [ ] = {
1 , // Mysterious
1 , // Hidden
1 , // Gloomy
1 , // Weird
1 , // Magical
1 , // Stone
1 , // Religious
1 , // Enchanted
1 , // Thaumaturgic
1 , // Fascinating
1 , // Cryptic
1 , // Magical
1 , // Eldritch
1 , // Eerie
1 , // Divine
1 , // Holy
1 , // Sacred
1 , // Spiritual
1 , // Spooky
1 , // Abandoned
1 , // Creepy
1 , // Quiet
1 , // Secluded
1 , // Ornate
1 , // Glimmering
1 , // Tainted
1 , // Oily
1 , // Glowing
1 , // Mendicant's
1 , // Sparkling
1 , // Town
1 , // Shimmering
1 , // Solar,
1 , // Murphy's
} ;
# define MAX_LVLS 24
/** Specifies the maximum dungeon level on which each shrine will appear. */
char shrinemax [ ] = {
MAX_LVLS , // Mysterious
MAX_LVLS , // Hidden
MAX_LVLS , // Gloomy
MAX_LVLS , // Weird
MAX_LVLS , // Magical
MAX_LVLS , // Stone
MAX_LVLS , // Religious
8 , // Enchanted
MAX_LVLS , // Thaumaturgic
MAX_LVLS , // Fascinating
MAX_LVLS , // Cryptic
MAX_LVLS , // Magical
MAX_LVLS , // Eldritch
MAX_LVLS , // Eerie
MAX_LVLS , // Divine
MAX_LVLS , // Holy
MAX_LVLS , // Sacred
MAX_LVLS , // Spiritual
MAX_LVLS , // Spooky
MAX_LVLS , // Abandoned
MAX_LVLS , // Creepy
MAX_LVLS , // Quiet
MAX_LVLS , // Secluded
MAX_LVLS , // Ornate
MAX_LVLS , // Glimmering
MAX_LVLS , // Tainted
MAX_LVLS , // Oily
MAX_LVLS , // Glowing
MAX_LVLS , // Mendicant's
MAX_LVLS , // Sparkling
MAX_LVLS , // Town
MAX_LVLS , // Shimmering
MAX_LVLS , // Solar,
MAX_LVLS , // Murphy's
} ;
/**
* Specifies the game type for which each shrine may appear .
* ShrineTypeAny - sp & mp
* ShrineTypeSingle - sp only
* ShrineTypeMulti - mp only
*/
enum shrine_gametype : uint8_t {
ShrineTypeAny ,
ShrineTypeSingle ,
ShrineTypeMulti ,
} ;
shrine_gametype shrineavail [ ] = {
ShrineTypeAny , // Mysterious
ShrineTypeAny , // Hidden
ShrineTypeSingle , // Gloomy
ShrineTypeSingle , // Weird
ShrineTypeAny , // Magical
ShrineTypeAny , // Stone
ShrineTypeAny , // Religious
ShrineTypeAny , // Enchanted
ShrineTypeSingle , // Thaumaturgic
ShrineTypeAny , // Fascinating
ShrineTypeAny , // Cryptic
ShrineTypeAny , // Magical
ShrineTypeAny , // Eldritch
ShrineTypeAny , // Eerie
ShrineTypeAny , // Divine
ShrineTypeAny , // Holy
ShrineTypeAny , // Sacred
ShrineTypeAny , // Spiritual
ShrineTypeMulti , // Spooky
ShrineTypeAny , // Abandoned
ShrineTypeAny , // Creepy
ShrineTypeAny , // Quiet
ShrineTypeAny , // Secluded
ShrineTypeAny , // Ornate
ShrineTypeAny , // Glimmering
ShrineTypeMulti , // Tainted
ShrineTypeAny , // Oily
ShrineTypeAny , // Glowing
ShrineTypeAny , // Mendicant's
ShrineTypeAny , // Sparkling
ShrineTypeAny , // Town
ShrineTypeAny , // Shimmering
ShrineTypeSingle , // Solar,
ShrineTypeAny , // Murphy's
} ;
/** Maps from book_id to book name. */
const char * const StoryBookName [ ] = {
N_ ( /* TRANSLATORS: Book Title */ " The Great Conflict " ) ,
N_ ( /* TRANSLATORS: Book Title */ " The Wages of Sin are War " ) ,
N_ ( /* TRANSLATORS: Book Title */ " The Tale of the Horadrim " ) ,
N_ ( /* TRANSLATORS: Book Title */ " The Dark Exile " ) ,
N_ ( /* TRANSLATORS: Book Title */ " The Sin War " ) ,
N_ ( /* TRANSLATORS: Book Title */ " The Binding of the Three " ) ,
N_ ( /* TRANSLATORS: Book Title */ " The Realms Beyond " ) ,
N_ ( /* TRANSLATORS: Book Title */ " Tale of the Three " ) ,
N_ ( /* TRANSLATORS: Book Title */ " The Black King " ) ,
N_ ( /* TRANSLATORS: Book Title */ " Journal: The Ensorcellment " ) ,
N_ ( /* TRANSLATORS: Book Title */ " Journal: The Meeting " ) ,
N_ ( /* TRANSLATORS: Book Title */ " Journal: The Tirade " ) ,
N_ ( /* TRANSLATORS: Book Title */ " Journal: His Power Grows " ) ,
N_ ( /* TRANSLATORS: Book Title */ " Journal: NA-KRUL " ) ,
N_ ( /* TRANSLATORS: Book Title */ " Journal: The End " ) ,
N_ ( /* TRANSLATORS: Book Title */ " A Spellbook " ) ,
} ;
/** Specifies the speech IDs of each dungeon type narrator book, for each player class. */
_speech_id StoryText [ 3 ] [ 3 ] = {
{ TEXT_BOOK11 , TEXT_BOOK12 , TEXT_BOOK13 } ,
{ TEXT_BOOK21 , TEXT_BOOK22 , TEXT_BOOK23 } ,
{ TEXT_BOOK31 , TEXT_BOOK32 , TEXT_BOOK33 }
} ;
bool RndLocOk ( int xp , int yp )
{
if ( dMonster [ xp ] [ yp ] ! = 0 )
return false ;
if ( dPlayer [ xp ] [ yp ] ! = 0 )
return false ;
if ( dObject [ xp ] [ yp ] ! = 0 )
return false ;
if ( ( dFlags [ xp ] [ yp ] & BFLAG_POPULATED ) ! = 0 )
return false ;
if ( nSolidTable [ dPiece [ xp ] [ yp ] ] )
return false ;
return leveltype ! = DTYPE_CATHEDRAL | | dPiece [ xp ] [ yp ] < = 126 | | dPiece [ xp ] [ yp ] > = 144 ;
}
bool CanPlaceWallTrap ( int xp , int yp )
{
if ( ( dFlags [ xp ] [ yp ] & BFLAG_POPULATED ) ! = 0 )
return false ;
return nTrapTable [ dPiece [ xp ] [ yp ] ] ;
}
void InitRndLocObj ( int min , int max , _object_id objtype )
{
int numobjs = GenerateRnd ( max - min ) + min ;
for ( int i = 0 ; i < numobjs ; i + + ) {
while ( true ) {
int xp = GenerateRnd ( 80 ) + 16 ;
int yp = GenerateRnd ( 80 ) + 16 ;
if ( RndLocOk ( xp - 1 , yp - 1 )
& & RndLocOk ( xp , yp - 1 )
& & RndLocOk ( xp + 1 , yp - 1 )
& & RndLocOk ( xp - 1 , yp )
& & RndLocOk ( xp , yp )
& & RndLocOk ( xp + 1 , yp )
& & RndLocOk ( xp - 1 , yp + 1 )
& & RndLocOk ( xp , yp + 1 )
& & RndLocOk ( xp + 1 , yp + 1 ) ) {
AddObject ( objtype , { xp , yp } ) ;
break ;
}
}
}
}
void InitRndLocBigObj ( int min , int max , _object_id objtype )
{
int numobjs = GenerateRnd ( max - min ) + min ;
for ( int i = 0 ; i < numobjs ; i + + ) {
while ( true ) {
int xp = GenerateRnd ( 80 ) + 16 ;
int yp = GenerateRnd ( 80 ) + 16 ;
if ( RndLocOk ( xp - 1 , yp - 2 )
& & RndLocOk ( xp , yp - 2 )
& & RndLocOk ( xp + 1 , yp - 2 )
& & RndLocOk ( xp - 1 , yp - 1 )
& & RndLocOk ( xp , yp - 1 )
& & RndLocOk ( xp + 1 , yp - 1 )
& & RndLocOk ( xp - 1 , yp )
& & RndLocOk ( xp , yp )
& & RndLocOk ( xp + 1 , yp )
& & RndLocOk ( xp - 1 , yp + 1 )
& & RndLocOk ( xp , yp + 1 )
& & RndLocOk ( xp + 1 , yp + 1 ) ) {
AddObject ( objtype , { xp , yp } ) ;
break ;
}
}
}
}
void InitRndLocObj5x5 ( int min , int max , _object_id objtype )
{
int numobjs = min + GenerateRnd ( max - min ) ;
for ( int i = 0 ; i < numobjs ; i + + ) {
int xp ;
int yp ;
int cnt = 0 ;
bool exit = false ;
while ( ! exit ) {
exit = true ;
xp = GenerateRnd ( 80 ) + 16 ;
yp = GenerateRnd ( 80 ) + 16 ;
for ( int n = - 2 ; n < = 2 ; n + + ) {
for ( int m = - 2 ; m < = 2 ; m + + ) {
if ( ! RndLocOk ( xp + m , yp + n ) )
exit = false ;
}
}
if ( ! exit ) {
cnt + + ;
if ( cnt > 20000 )
return ;
}
}
AddObject ( objtype , { xp , yp } ) ;
}
}
void ClrAllObjects ( )
{
memset ( Objects , 0 , sizeof ( Objects ) ) ;
ActiveObjectCount = 0 ;
for ( int i = 0 ; i < MAXOBJECTS ; i + + ) {
AvailableObjects [ i ] = i ;
}
memset ( ActiveObjects , 0 , sizeof ( ActiveObjects ) ) ;
trapdir = 0 ;
trapid = 1 ;
leverid = 1 ;
}
void AddTortures ( )
{
for ( int oy = 0 ; oy < MAXDUNY ; oy + + ) {
for ( int ox = 0 ; ox < MAXDUNX ; ox + + ) {
if ( dPiece [ ox ] [ oy ] = = 367 ) {
AddObject ( OBJ_TORTURE1 , { ox , oy + 1 } ) ;
AddObject ( OBJ_TORTURE3 , { ox + 2 , oy - 1 } ) ;
AddObject ( OBJ_TORTURE2 , { ox , oy + 3 } ) ;
AddObject ( OBJ_TORTURE4 , { ox + 4 , oy - 1 } ) ;
AddObject ( OBJ_TORTURE5 , { ox , oy + 5 } ) ;
AddObject ( OBJ_TNUDEM1 , { ox + 1 , oy + 3 } ) ;
AddObject ( OBJ_TNUDEM2 , { ox + 4 , oy + 5 } ) ;
AddObject ( OBJ_TNUDEM3 , { ox + 2 , oy } ) ;
AddObject ( OBJ_TNUDEM4 , { ox + 3 , oy + 2 } ) ;
AddObject ( OBJ_TNUDEW1 , { ox + 2 , oy + 4 } ) ;
AddObject ( OBJ_TNUDEW2 , { ox + 2 , oy + 1 } ) ;
AddObject ( OBJ_TNUDEW3 , { ox + 4 , oy + 2 } ) ;
}
}
}
}
void AddCandles ( )
{
int tx = Quests [ Q_PWATER ] . position . x ;
int ty = Quests [ Q_PWATER ] . position . y ;
AddObject ( OBJ_STORYCANDLE , { tx - 2 , ty + 1 } ) ;
AddObject ( OBJ_STORYCANDLE , { tx + 3 , ty + 1 } ) ;
AddObject ( OBJ_STORYCANDLE , { tx - 1 , ty + 2 } ) ;
AddObject ( OBJ_STORYCANDLE , { tx + 2 , ty + 2 } ) ;
}
/**
* @ brief Attempts to spawn a book somewhere on the current floor which when activated will change a region of the map .
*
* This object acts like a lever and will cause a change to the map based on what quest is active . The exact effect is
* determined by OperateBookLever ( ) .
*
* @ param affectedArea The map region to be updated when this object is activated by the player .
* @ param msg The quest text to play when the player activates the book .
*/
void AddBookLever ( Rectangle affectedArea , _speech_id msg )
{
int cnt = 0 ;
int xp ;
int yp ;
bool exit = false ;
while ( ! exit ) {
exit = true ;
xp = GenerateRnd ( 80 ) + 16 ;
yp = GenerateRnd ( 80 ) + 16 ;
for ( int n = - 2 ; n < = 2 ; n + + ) {
for ( int m = - 2 ; m < = 2 ; m + + ) {
if ( ! RndLocOk ( xp + m , yp + n ) )
exit = false ;
}
}
if ( ! exit ) {
cnt + + ;
if ( cnt > 20000 )
return ;
}
}
if ( Quests [ Q_BLIND ] . IsAvailable ( ) )
AddObject ( OBJ_BLINDBOOK , { xp , yp } ) ;
if ( Quests [ Q_WARLORD ] . IsAvailable ( ) )
AddObject ( OBJ_STEELTOME , { xp , yp } ) ;
if ( Quests [ Q_BLOOD ] . IsAvailable ( ) ) {
xp = 2 * setpc_x + 25 ;
yp = 2 * setpc_y + 40 ;
AddObject ( OBJ_BLOODBOOK , { xp , yp } ) ;
}
int ob = dObject [ xp ] [ yp ] - 1 ;
Objects [ ob ] . InitializeQuestBook ( affectedArea , leverid , msg ) ;
leverid + + ;
}
void InitRndBarrels ( )
{
/** number of groups of barrels to generate */
int numobjs = GenerateRnd ( 5 ) + 3 ;
for ( int i = 0 ; i < numobjs ; i + + ) {
int xp ;
int yp ;
do {
xp = GenerateRnd ( 80 ) + 16 ;
yp = GenerateRnd ( 80 ) + 16 ;
} while ( ! RndLocOk ( xp , yp ) ) ;
_object_id o = ( GenerateRnd ( 4 ) ! = 0 ) ? OBJ_BARREL : OBJ_BARRELEX ;
AddObject ( o , { xp , yp } ) ;
bool found = true ;
/** regulates chance to stop placing barrels in current group */
int p = 0 ;
/** number of barrels in current group */
int c = 1 ;
while ( GenerateRnd ( p ) = = 0 & & found ) {
/** number of tries of placing next barrel in current group */
int t = 0 ;
found = false ;
while ( true ) {
if ( t > = 3 )
break ;
int dir = GenerateRnd ( 8 ) ;
xp + = bxadd [ dir ] ;
yp + = byadd [ dir ] ;
found = RndLocOk ( xp , yp ) ;
t + + ;
if ( found )
break ;
}
if ( found ) {
o = ( GenerateRnd ( 5 ) ! = 0 ) ? OBJ_BARREL : OBJ_BARRELEX ;
AddObject ( o , { xp , yp } ) ;
c + + ;
}
p = c / 2 ;
}
}
}
void AddCryptObjects ( int x1 , int y1 , int x2 , int y2 )
{
for ( int j = y1 ; j < y2 ; j + + ) {
for ( int i = x1 ; i < x2 ; i + + ) {
int pn = dPiece [ i ] [ j ] ;
if ( pn = = 77 )
AddObject ( OBJ_L1LDOOR , { i , j } ) ;
if ( pn = = 80 )
AddObject ( OBJ_L1RDOOR , { i , j } ) ;
}
}
}
void AddL3Objs ( int x1 , int y1 , int x2 , int y2 )
{
for ( int j = y1 ; j < y2 ; j + + ) {
for ( int i = x1 ; i < x2 ; i + + ) {
int pn = dPiece [ i ] [ j ] ;
if ( pn = = 531 )
AddObject ( OBJ_L3LDOOR , { i , j } ) ;
if ( pn = = 534 )
AddObject ( OBJ_L3RDOOR , { i , j } ) ;
}
}
}
bool TorchLocOK ( int xp , int yp )
{
return ( dFlags [ xp ] [ yp ] & BFLAG_POPULATED ) = = 0 ;
}
void AddL2Torches ( )
{
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) {
if ( ! TorchLocOK ( i , j ) )
continue ;
int pn = dPiece [ i ] [ j ] ;
if ( pn = = 1 & & GenerateRnd ( 3 ) = = 0 )
AddObject ( OBJ_TORCHL2 , { i , j } ) ;
if ( pn = = 5 & & GenerateRnd ( 3 ) = = 0 )
AddObject ( OBJ_TORCHR2 , { i , j } ) ;
if ( pn = = 37 & & GenerateRnd ( 10 ) = = 0 & & dObject [ i - 1 ] [ j ] = = 0 )
AddObject ( OBJ_TORCHL , { i - 1 , j } ) ;
if ( pn = = 41 & & GenerateRnd ( 10 ) = = 0 & & dObject [ i ] [ j - 1 ] = = 0 )
AddObject ( OBJ_TORCHR , { i , j - 1 } ) ;
}
}
}
void AddObjTraps ( )
{
int rndv ;
if ( currlevel = = 1 )
rndv = 10 ;
if ( currlevel > = 2 )
rndv = 15 ;
if ( currlevel > = 5 )
rndv = 20 ;
if ( currlevel > = 7 )
rndv = 25 ;
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) {
if ( dObject [ i ] [ j ] < = 0 | | GenerateRnd ( 100 ) > = rndv )
continue ;
int8_t oi = dObject [ i ] [ j ] - 1 ;
if ( ! AllObjects [ Objects [ oi ] . _otype ] . oTrapFlag )
continue ;
if ( GenerateRnd ( 2 ) = = 0 ) {
int xp = i - 1 ;
while ( IsTileNotSolid ( { xp , j } ) ) // BUGFIX: check if xp >= 0
xp - - ;
if ( ! CanPlaceWallTrap ( xp , j ) | | i - xp < = 1 )
continue ;
AddObject ( OBJ_TRAPL , { xp , j } ) ;
int8_t oiTrap = dObject [ xp ] [ j ] - 1 ;
Objects [ oiTrap ] . _oVar1 = i ;
Objects [ oiTrap ] . _oVar2 = j ;
Objects [ oi ] . _oTrapFlag = true ;
} else {
int yp = j - 1 ;
while ( IsTileNotSolid ( { i , yp } ) ) // BUGFIX: check if yp >= 0
yp - - ;
if ( ! CanPlaceWallTrap ( i , yp ) | | j - yp < = 1 )
continue ;
AddObject ( OBJ_TRAPR , { i , yp } ) ;
int8_t oiTrap = dObject [ i ] [ yp ] - 1 ;
Objects [ oiTrap ] . _oVar1 = i ;
Objects [ oiTrap ] . _oVar2 = j ;
Objects [ oi ] . _oTrapFlag = true ;
}
}
}
}
void AddChestTraps ( )
{
for ( int j = 0 ; j < MAXDUNY ; j + + ) {
for ( int i = 0 ; i < MAXDUNX ; i + + ) { // NOLINT(modernize-loop-convert)
if ( dObject [ i ] [ j ] > 0 ) {
int8_t oi = dObject [ i ] [ j ] - 1 ;
if ( Objects [ oi ] . _otype > = OBJ_CHEST1 & & Objects [ oi ] . _otype < = OBJ_CHEST3 & & ! Objects [ oi ] . _oTrapFlag & & GenerateRnd ( 100 ) < 10 ) {
switch ( Objects [ oi ] . _otype ) {
case OBJ_CHEST1 :
Objects [ oi ] . _otype = OBJ_TCHEST1 ;
break ;
case OBJ_CHEST2 :
Objects [ oi ] . _otype = OBJ_TCHEST2 ;
break ;
case OBJ_CHEST3 :
Objects [ oi ] . _otype = OBJ_TCHEST3 ;
break ;
default :
break ;
}
Objects [ oi ] . _oTrapFlag = true ;
if ( leveltype = = DTYPE_CATACOMBS ) {
Objects [ oi ] . _oVar4 = GenerateRnd ( 2 ) ;
} else {
Objects [ oi ] . _oVar4 = GenerateRnd ( gbIsHellfire ? 6 : 3 ) ;
}
}
}
}
}
}
void LoadMapObjects ( const char * path , Point start , Rectangle mapRange , int leveridx )
{
LoadingMapObjects = true ;
ApplyObjectLighting = true ;
auto dunData = LoadFileInMem < uint16_t > ( path ) ;
int width = SDL_SwapLE16 ( dunData [ 0 ] ) ;
int height = SDL_SwapLE16 ( dunData [ 1 ] ) ;
int layer2Offset = 2 + width * height ;
// The rest of the layers are at dPiece scale
width * = 2 ;
height * = 2 ;
const uint16_t * objectLayer = & dunData [ layer2Offset + width * height * 2 ] ;
start + = Displacement { 16 , 16 } ;
for ( int j = 0 ; j < height ; j + + ) {
for ( int i = 0 ; i < width ; i + + ) {
uint8_t objectId = SDL_SwapLE16 ( objectLayer [ j * width + i ] ) ;
if ( objectId ! = 0 ) {
Point mapPos = start + Displacement { i , j } ;
AddObject ( ObjTypeConv [ objectId ] , mapPos ) ;
ObjectAtPosition ( mapPos ) . InitializeLoadedObject ( mapRange , leveridx ) ;
}
}
}
ApplyObjectLighting = false ;
LoadingMapObjects = false ;
}
void LoadMapObjs ( const char * path , Point start )
{
LoadingMapObjects = true ;
ApplyObjectLighting = true ;
auto dunData = LoadFileInMem < uint16_t > ( path ) ;
int width = SDL_SwapLE16 ( dunData [ 0 ] ) ;
int height = SDL_SwapLE16 ( dunData [ 1 ] ) ;
int layer2Offset = 2 + width * height ;
// The rest of the layers are at dPiece scale
width * = 2 ;
height * = 2 ;
const uint16_t * objectLayer = & dunData [ layer2Offset + width * height * 2 ] ;
start + = Displacement { 16 , 16 } ;
for ( int j = 0 ; j < height ; j + + ) {
for ( int i = 0 ; i < width ; i + + ) {
uint8_t objectId = SDL_SwapLE16 ( objectLayer [ j * width + i ] ) ;
if ( objectId ! = 0 ) {
AddObject ( ObjTypeConv [ objectId ] , start + Displacement { i , j } ) ;
}
}
}
ApplyObjectLighting = false ;
LoadingMapObjects = false ;
}
void AddDiabObjs ( )
{
LoadMapObjects ( " Levels \\ L4Data \\ diab1.DUN " , { 2 * diabquad1x , 2 * diabquad1y } , { { diabquad2x , diabquad2y } , { 11 , 12 } } , 1 ) ;
LoadMapObjects ( " Levels \\ L4Data \\ diab2a.DUN " , { 2 * diabquad2x , 2 * diabquad2y } , { { diabquad3x , diabquad3y } , { 11 , 11 } } , 2 ) ;
LoadMapObjects ( " Levels \\ L4Data \\ diab3a.DUN " , { 2 * diabquad3x , 2 * diabquad3y } , { { diabquad4x , diabquad4y } , { 9 , 9 } } , 3 ) ;
}
void AddCryptObject ( int i , int a2 )
{
if ( a2 > 5 ) {
auto & myPlayer = Players [ MyPlayerId ] ;
switch ( a2 ) {
case 6 :
switch ( myPlayer . _pClass ) {
case HeroClass : : Warrior :
case HeroClass : : Barbarian :
Objects [ i ] . _oVar2 = TEXT_BOOKA ;
break ;
case HeroClass : : Rogue :
Objects [ i ] . _oVar2 = TEXT_RBOOKA ;
break ;
case HeroClass : : Sorcerer :
Objects [ i ] . _oVar2 = TEXT_MBOOKA ;
break ;
case HeroClass : : Monk :
Objects [ i ] . _oVar2 = TEXT_OBOOKA ;
break ;
case HeroClass : : Bard :
Objects [ i ] . _oVar2 = TEXT_BBOOKA ;
break ;
}
break ;
case 7 :
switch ( myPlayer . _pClass ) {
case HeroClass : : Warrior :
case HeroClass : : Barbarian :
Objects [ i ] . _oVar2 = TEXT_BOOKB ;
break ;
case HeroClass : : Rogue :
Objects [ i ] . _oVar2 = TEXT_RBOOKB ;
break ;
case HeroClass : : Sorcerer :
Objects [ i ] . _oVar2 = TEXT_MBOOKB ;
break ;
case HeroClass : : Monk :
Objects [ i ] . _oVar2 = TEXT_OBOOKB ;
break ;
case HeroClass : : Bard :
Objects [ i ] . _oVar2 = TEXT_BBOOKB ;
break ;
}
break ;
case 8 :
switch ( myPlayer . _pClass ) {
case HeroClass : : Warrior :
case HeroClass : : Barbarian :
Objects [ i ] . _oVar2 = TEXT_BOOKC ;
break ;
case HeroClass : : Rogue :
Objects [ i ] . _oVar2 = TEXT_RBOOKC ;
break ;
case HeroClass : : Sorcerer :
Objects [ i ] . _oVar2 = TEXT_MBOOKC ;
break ;
case HeroClass : : Monk :
Objects [ i ] . _oVar2 = TEXT_OBOOKC ;
break ;
case HeroClass : : Bard :
Objects [ i ] . _oVar2 = TEXT_BBOOKC ;
break ;
}
break ;
}
Objects [ i ] . _oVar3 = 15 ;
Objects [ i ] . _oVar8 = a2 ;
} else {
Objects [ i ] . _oVar2 = a2 + TEXT_SKLJRN ;
Objects [ i ] . _oVar3 = a2 + 9 ;
Objects [ i ] . _oVar8 = 0 ;
}
Objects [ i ] . _oVar1 = 1 ;
Objects [ i ] . _oAnimFrame = 5 - 2 * Objects [ i ] . _oVar1 ;
Objects [ i ] . _oVar4 = Objects [ i ] . _oAnimFrame + 1 ;
}
void SetupObject ( int i , Point position , _object_id ot )
{
Objects [ i ] . _otype = ot ;
object_graphic_id ofi = AllObjects [ ot ] . ofindex ;
Objects [ i ] . position = position ;
const auto & found = std : : find ( std : : begin ( ObjFileList ) , std : : end ( ObjFileList ) , ofi ) ;
if ( found = = std : : end ( ObjFileList ) ) {
LogCritical ( " Unable to find object_graphic_id {} in list of objects to load, level generation error. " , ofi ) ;
return ;
}
const int j = std : : distance ( std : : begin ( ObjFileList ) , found ) ;
Objects [ i ] . _oAnimData = pObjCels [ j ] . get ( ) ;
Objects [ i ] . _oAnimFlag = AllObjects [ ot ] . oAnimFlag ;
if ( AllObjects [ ot ] . oAnimFlag ! = 0 ) {
Objects [ i ] . _oAnimDelay = AllObjects [ ot ] . oAnimDelay ;
Objects [ i ] . _oAnimCnt = GenerateRnd ( AllObjects [ ot ] . oAnimDelay ) ;
Objects [ i ] . _oAnimLen = AllObjects [ ot ] . oAnimLen ;
Objects [ i ] . _oAnimFrame = GenerateRnd ( AllObjects [ ot ] . oAnimLen - 1 ) + 1 ;
} else {
Objects [ i ] . _oAnimDelay = 1000 ;
Objects [ i ] . _oAnimCnt = 0 ;
Objects [ i ] . _oAnimLen = AllObjects [ ot ] . oAnimLen ;
Objects [ i ] . _oAnimFrame = AllObjects [ ot ] . oAnimDelay ;
}
Objects [ i ] . _oAnimWidth = AllObjects [ ot ] . oAnimWidth ;
Objects [ i ] . _oSolidFlag = AllObjects [ ot ] . oSolidFlag ;
Objects [ i ] . _oMissFlag = AllObjects [ ot ] . oMissFlag ;
Objects [ i ] . _oLight = AllObjects [ ot ] . oLightFlag ;
Objects [ i ] . _oDelFlag = false ;
Objects [ i ] . _oBreak = AllObjects [ ot ] . oBreak ;
Objects [ i ] . _oSelFlag = AllObjects [ ot ] . oSelFlag ;
Objects [ i ] . _oPreFlag = false ;
Objects [ i ] . _oTrapFlag = false ;
Objects [ i ] . _oDoorFlag = false ;
}
void AddCryptBook ( _object_id ot , int v2 , int ox , int oy )
{
if ( ActiveObjectCount > = MAXOBJECTS )
return ;
int oi = AvailableObjects [ 0 ] ;
AvailableObjects [ 0 ] = AvailableObjects [ MAXOBJECTS - 1 - ActiveObjectCount ] ;
ActiveObjects [ ActiveObjectCount ] = oi ;
dObject [ ox ] [ oy ] = oi + 1 ;
SetupObject ( oi , { ox , oy } , ot ) ;
AddCryptObject ( oi , v2 ) ;
ActiveObjectCount + + ;
}
void AddCryptStoryBook ( int s )
{
int cnt = 0 ;
int xp ;
int yp ;
bool exit = false ;
while ( ! exit ) {
exit = true ;
xp = GenerateRnd ( 80 ) + 16 ;
yp = GenerateRnd ( 80 ) + 16 ;
for ( int n = - 2 ; n < = 2 ; n + + ) {
for ( int m = - 3 ; m < = 3 ; m + + ) {
if ( ! RndLocOk ( xp + m , yp + n ) )
exit = false ;
}
}
if ( ! exit ) {
cnt + + ;
if ( cnt > 20000 )
return ;
}
}
AddCryptBook ( OBJ_STORYBOOK , s , xp , yp ) ;
AddObject ( OBJ_STORYCANDLE , { xp - 2 , yp + 1 } ) ;
AddObject ( OBJ_STORYCANDLE , { xp - 2 , yp } ) ;
AddObject ( OBJ_STORYCANDLE , { xp - 1 , yp - 1 } ) ;
AddObject ( OBJ_STORYCANDLE , { xp + 1 , yp - 1 } ) ;
AddObject ( OBJ_STORYCANDLE , { xp + 2 , yp } ) ;
AddObject ( OBJ_STORYCANDLE , { xp + 2 , yp + 1 } ) ;
}
void AddNakrulBook ( int a1 , int a2 , int a3 )
{
AddCryptBook ( OBJ_STORYBOOK , a1 , a2 , a3 ) ;
}
void AddNakrulGate ( )
{
AddNakrulLeaver ( ) ;
switch ( GenerateRnd ( 6 ) ) {
case 0 :
AddNakrulBook ( 6 , UberRow + 3 , UberCol ) ;
AddNakrulBook ( 7 , UberRow + 2 , UberCol - 3 ) ;
AddNakrulBook ( 8 , UberRow + 2 , UberCol + 2 ) ;
break ;
case 1 :
AddNakrulBook ( 6 , UberRow + 3 , UberCol ) ;
AddNakrulBook ( 8 , UberRow + 2 , UberCol - 3 ) ;
AddNakrulBook ( 7 , UberRow + 2 , UberCol + 2 ) ;
break ;
case 2 :
AddNakrulBook ( 7 , UberRow + 3 , UberCol ) ;
AddNakrulBook ( 6 , UberRow + 2 , UberCol - 3 ) ;
AddNakrulBook ( 8 , UberRow + 2 , UberCol + 2 ) ;
break ;
case 3 :
AddNakrulBook ( 7 , UberRow + 3 , UberCol ) ;
AddNakrulBook ( 8 , UberRow + 2 , UberCol - 3 ) ;
AddNakrulBook ( 6 , UberRow + 2 , UberCol + 2 ) ;
break ;
case 4 :
AddNakrulBook ( 8 , UberRow + 3 , UberCol ) ;
AddNakrulBook ( 7 , UberRow + 2 , UberCol - 3 ) ;
AddNakrulBook ( 6 , UberRow + 2 , UberCol + 2 ) ;
break ;
case 5 :
AddNakrulBook ( 8 , UberRow + 3 , UberCol ) ;
AddNakrulBook ( 6 , UberRow + 2 , UberCol - 3 ) ;
AddNakrulBook ( 7 , UberRow + 2 , UberCol + 2 ) ;
break ;
}
}
void AddStoryBooks ( )
{
int cnt = 0 ;
int xp ;
int yp ;
bool done = false ;
while ( ! done ) {
done = true ;
xp = GenerateRnd ( 80 ) + 16 ;
yp = GenerateRnd ( 80 ) + 16 ;
for ( int yy = - 2 ; yy < = 2 ; yy + + ) {
for ( int xx = - 3 ; xx < = 3 ; xx + + ) {
if ( ! RndLocOk ( xx + xp , yy + yp ) )
done = false ;
}
}
if ( ! done ) {
cnt + + ;
if ( cnt > 20000 )
return ;
}
}
AddObject ( OBJ_STORYBOOK , { xp , yp } ) ;
AddObject ( OBJ_STORYCANDLE , { xp - 2 , yp + 1 } ) ;
AddObject ( OBJ_STORYCANDLE , { xp - 2 , yp } ) ;
AddObject ( OBJ_STORYCANDLE , { xp - 1 , yp - 1 } ) ;
AddObject ( OBJ_STORYCANDLE , { xp + 1 , yp - 1 } ) ;
AddObject ( OBJ_STORYCANDLE , { xp + 2 , yp } ) ;
AddObject ( OBJ_STORYCANDLE , { xp + 2 , yp + 1 } ) ;
}
void AddHookedBodies ( int freq )
{
for ( int j = 0 ; j < DMAXY ; j + + ) {
int jj = 16 + j * 2 ;
for ( int i = 0 ; i < DMAXX ; i + + ) {
int ii = 16 + i * 2 ;
if ( dungeon [ i ] [ j ] ! = 1 & & dungeon [ i ] [ j ] ! = 2 )
continue ;
if ( GenerateRnd ( freq ) ! = 0 )
continue ;
if ( ! SkipThemeRoom ( i , j ) )
continue ;
if ( dungeon [ i ] [ j ] = = 1 & & dungeon [ i + 1 ] [ j ] = = 6 ) {
switch ( GenerateRnd ( 3 ) ) {
case 0 :
AddObject ( OBJ_TORTURE1 , { ii + 1 , jj } ) ;
break ;
case 1 :
AddObject ( OBJ_TORTURE2 , { ii + 1 , jj } ) ;
break ;
case 2 :
AddObject ( OBJ_TORTURE5 , { ii + 1 , jj } ) ;
break ;
}
continue ;
}
if ( dungeon [ i ] [ j ] = = 2 & & dungeon [ i ] [ j + 1 ] = = 6 ) {
switch ( GenerateRnd ( 2 ) ) {
case 0 :
AddObject ( OBJ_TORTURE3 , { ii , jj } ) ;
break ;
case 1 :
AddObject ( OBJ_TORTURE4 , { ii , jj } ) ;
break ;
}
}
}
}
}
void AddL4Goodies ( )
{
AddHookedBodies ( 6 ) ;
InitRndLocObj ( 2 , 6 , OBJ_TNUDEM1 ) ;
InitRndLocObj ( 2 , 6 , OBJ_TNUDEM2 ) ;
InitRndLocObj ( 2 , 6 , OBJ_TNUDEM3 ) ;
InitRndLocObj ( 2 , 6 , OBJ_TNUDEM4 ) ;
InitRndLocObj ( 2 , 6 , OBJ_TNUDEW1 ) ;
InitRndLocObj ( 2 , 6 , OBJ_TNUDEW2 ) ;
InitRndLocObj ( 2 , 6 , OBJ_TNUDEW3 ) ;
InitRndLocObj ( 2 , 6 , OBJ_DECAP ) ;
InitRndLocObj ( 1 , 3 , OBJ_CAULDRON ) ;
}
void AddLazStand ( )
{
int cnt = 0 ;
int xp ;
int yp ;
bool found = false ;
while ( ! found ) {
found = true ;
xp = GenerateRnd ( 80 ) + 16 ;
yp = GenerateRnd ( 80 ) + 16 ;
for ( int yy = - 3 ; yy < = 3 ; yy + + ) {
for ( int xx = - 2 ; xx < = 3 ; xx + + ) {
if ( ! RndLocOk ( xp + xx , yp + yy ) )
found = false ;
}
}
if ( ! found ) {
cnt + + ;
if ( cnt > 10000 ) {
InitRndLocObj ( 1 , 1 , OBJ_LAZSTAND ) ;
return ;
}
}
}
AddObject ( OBJ_LAZSTAND , { xp , yp } ) ;
AddObject ( OBJ_TNUDEM2 , { xp , yp + 2 } ) ;
AddObject ( OBJ_STORYCANDLE , { xp + 1 , yp + 2 } ) ;
AddObject ( OBJ_TNUDEM3 , { xp + 2 , yp + 2 } ) ;
AddObject ( OBJ_TNUDEW1 , { xp , yp - 2 } ) ;
AddObject ( OBJ_STORYCANDLE , { xp + 1 , yp - 2 } ) ;
AddObject ( OBJ_TNUDEW2 , { xp + 2 , yp - 2 } ) ;
AddObject ( OBJ_STORYCANDLE , { xp - 1 , yp - 1 } ) ;
AddObject ( OBJ_TNUDEW3 , { xp - 1 , yp } ) ;
AddObject ( OBJ_STORYCANDLE , { xp - 1 , yp + 1 } ) ;
}
void DeleteObject ( int oi , int i )
{
int ox = Objects [ oi ] . position . x ;
int oy = Objects [ oi ] . position . y ;
dObject [ ox ] [ oy ] = 0 ;
AvailableObjects [ - ActiveObjectCount + MAXOBJECTS ] = oi ;
ActiveObjectCount - - ;
if ( pcursobj = = oi ) // Unselect object if this was highlighted by player
pcursobj = - 1 ;
if ( ActiveObjectCount > 0 & & i ! = ActiveObjectCount )
ActiveObjects [ i ] = ActiveObjects [ ActiveObjectCount ] ;
}
void AddL1Door ( int i , Point position , _object_id objectType )
{
Objects [ i ] . _oDoorFlag = true ;
if ( objectType = = _object_id : : OBJ_L1LDOOR ) {
Objects [ i ] . _oVar1 = dPiece [ position . x ] [ position . y ] ;
Objects [ i ] . _oVar2 = dPiece [ position . x ] [ position . y - 1 ] ;
} else { //_object_id::OBJ_L1RDOOR
Objects [ i ] . _oVar1 = dPiece [ position . x ] [ position . y ] ;
Objects [ i ] . _oVar2 = dPiece [ position . x - 1 ] [ position . y ] ;
}
Objects [ i ] . _oVar4 = 0 ;
}
void AddChest ( int i , int t )
{
if ( GenerateRnd ( 2 ) = = 0 )
Objects [ i ] . _oAnimFrame + = 3 ;
Objects [ i ] . _oRndSeed = AdvanceRndSeed ( ) ;
switch ( t ) {
case OBJ_CHEST1 :
case OBJ_TCHEST1 :
if ( setlevel ) {
Objects [ i ] . _oVar1 = 1 ;
break ;
}
Objects [ i ] . _oVar1 = GenerateRnd ( 2 ) ;
break ;
case OBJ_TCHEST2 :
case OBJ_CHEST2 :
if ( setlevel ) {
Objects [ i ] . _oVar1 = 2 ;
break ;
}
Objects [ i ] . _oVar1 = GenerateRnd ( 3 ) ;
break ;
case OBJ_TCHEST3 :
case OBJ_CHEST3 :
if ( setlevel ) {
Objects [ i ] . _oVar1 = 3 ;
break ;
}
Objects [ i ] . _oVar1 = GenerateRnd ( 4 ) ;
break ;
}
Objects [ i ] . _oVar2 = GenerateRnd ( 8 ) ;
}
void ObjSetMicro ( Point position , int pn )
{
dPiece [ position . x ] [ position . y ] = pn ;
pn - - ;
int blocks = leveltype ! = DTYPE_HELL ? 10 : 16 ;
uint16_t * piece = & pLevelPieces [ blocks * pn ] ;
MICROS & micros = dpiece_defs_map_2 [ position . x ] [ position . y ] ;
for ( int i = 0 ; i < blocks ; i + + ) {
micros . mt [ i ] = SDL_SwapLE16 ( piece [ blocks - 2 + ( i & 1 ) - ( i & 0xE ) ] ) ;
}
}
void AddL2Door ( int i , Point position , _object_id objectType )
{
Objects [ i ] . _oDoorFlag = true ;
if ( objectType = = OBJ_L2LDOOR )
ObjSetMicro ( position , 538 ) ;
else
ObjSetMicro ( position , 540 ) ;
dSpecial [ position . x ] [ position . y ] = 0 ;
Objects [ i ] . _oVar4 = 0 ;
}
void AddL3Door ( int i , Point position , _object_id objectType )
{
Objects [ i ] . _oDoorFlag = true ;
if ( objectType = = OBJ_L3LDOOR )
ObjSetMicro ( position , 531 ) ;
else
ObjSetMicro ( position , 534 ) ;
Objects [ i ] . _oVar4 = 0 ;
}
void AddSarc ( int i )
{
dObject [ Objects [ i ] . position . x ] [ Objects [ i ] . position . y - 1 ] = - ( i + 1 ) ;
Objects [ i ] . _oVar1 = GenerateRnd ( 10 ) ;
Objects [ i ] . _oRndSeed = AdvanceRndSeed ( ) ;
if ( Objects [ i ] . _oVar1 > = 8 )
Objects [ i ] . _oVar2 = PreSpawnSkeleton ( ) ;
}
void AddFlameTrap ( int i )
{
Objects [ i ] . _oVar1 = trapid ;
Objects [ i ] . _oVar2 = 0 ;
Objects [ i ] . _oVar3 = trapdir ;
Objects [ i ] . _oVar4 = 0 ;
}
void AddFlameLvr ( int i )
{
Objects [ i ] . _oVar1 = trapid ;
Objects [ i ] . _oVar2 = MIS_FLAMEC ;
}
void AddTrap ( int i )
{
int mt = currlevel / 3 + 1 ;
if ( currlevel > 16 ) {
mt = ( currlevel - 4 ) / 3 + 1 ;
}
if ( currlevel > 20 ) {
mt = ( currlevel - 8 ) / 3 + 1 ;
}
mt = GenerateRnd ( mt ) ;
if ( mt = = 0 )
Objects [ i ] . _oVar3 = MIS_ARROW ;
if ( mt = = 1 )
Objects [ i ] . _oVar3 = MIS_FIREBOLT ;
if ( mt = = 2 )
Objects [ i ] . _oVar3 = MIS_LIGHTCTRL ;
Objects [ i ] . _oVar4 = 0 ;
}
void AddObjectLight ( int i , int r )
{
if ( ApplyObjectLighting ) {
DoLighting ( Objects [ i ] . position , r , - 1 ) ;
Objects [ i ] . _oVar1 = - 1 ;
} else {
Objects [ i ] . _oVar1 = 0 ;
}
}
void AddBarrel ( int i , int t )
{
Objects [ i ] . _oVar1 = 0 ;
Objects [ i ] . _oRndSeed = AdvanceRndSeed ( ) ;
Objects [ i ] . _oVar2 = ( t = = OBJ_BARRELEX ) ? 0 : GenerateRnd ( 10 ) ;
Objects [ i ] . _oVar3 = GenerateRnd ( 3 ) ;
if ( Objects [ i ] . _oVar2 > = 8 )
Objects [ i ] . _oVar4 = PreSpawnSkeleton ( ) ;
}
void AddShrine ( int i )
{
bool slist [ NumberOfShrineTypes ] ;
Objects [ i ] . _oPreFlag = true ;
int shrines = gbIsHellfire ? NumberOfShrineTypes : 26 ;
for ( int j = 0 ; j < shrines ; j + + ) {
slist [ j ] = currlevel > = shrinemin [ j ] & & currlevel < = shrinemax [ j ] ;
if ( gbIsMultiplayer & & shrineavail [ j ] = = ShrineTypeSingle ) {
slist [ j ] = false ;
} else if ( ! gbIsMultiplayer & & shrineavail [ j ] = = ShrineTypeMulti ) {
slist [ j ] = false ;
}
}
int val ;
do {
val = GenerateRnd ( shrines ) ;
} while ( ! slist [ val ] ) ;
Objects [ i ] . _oVar1 = val ;
if ( GenerateRnd ( 2 ) ! = 0 ) {
Objects [ i ] . _oAnimFrame = 12 ;
Objects [ i ] . _oAnimLen = 22 ;
}
}
void AddBookcase ( int i )
{
Objects [ i ] . _oRndSeed = AdvanceRndSeed ( ) ;
Objects [ i ] . _oPreFlag = true ;
}
void AddBookstand ( int i )
{
Objects [ i ] . _oRndSeed = AdvanceRndSeed ( ) ;
}
void AddBloodFtn ( int i )
{
Objects [ i ] . _oRndSeed = AdvanceRndSeed ( ) ;
}
void AddPurifyingFountain ( int i )
{
int ox = Objects [ i ] . position . x ;
int oy = Objects [ i ] . position . y ;
dObject [ ox ] [ oy - 1 ] = - ( i + 1 ) ;
dObject [ ox - 1 ] [ oy ] = - ( i + 1 ) ;
dObject [ ox - 1 ] [ oy - 1 ] = - ( i + 1 ) ;
Objects [ i ] . _oRndSeed = AdvanceRndSeed ( ) ;
}
void AddArmorStand ( int i )
{
if ( ! armorFlag ) {
Objects [ i ] . _oAnimFlag = 2 ;
Objects [ i ] . _oSelFlag = 0 ;
}
Objects [ i ] . _oRndSeed = AdvanceRndSeed ( ) ;
}
void AddGoatShrine ( int i )
{
Objects [ i ] . _oRndSeed = AdvanceRndSeed ( ) ;
}
void AddCauldron ( int i )
{
Objects [ i ] . _oRndSeed = AdvanceRndSeed ( ) ;
}
void AddMurkyFountain ( int i )
{
int ox = Objects [ i ] . position . x ;
int oy = Objects [ i ] . position . y ;
dObject [ ox ] [ oy - 1 ] = - ( i + 1 ) ;
dObject [ ox - 1 ] [ oy ] = - ( i + 1 ) ;
dObject [ ox - 1 ] [ oy - 1 ] = - ( i + 1 ) ;
Objects [ i ] . _oRndSeed = AdvanceRndSeed ( ) ;
}
void AddTearFountain ( int i )
{
Objects [ i ] . _oRndSeed = AdvanceRndSeed ( ) ;
}
void AddDecap ( int i )
{
Objects [ i ] . _oRndSeed = AdvanceRndSeed ( ) ;
Objects [ i ] . _oAnimFrame = GenerateRnd ( 8 ) + 1 ;
Objects [ i ] . _oPreFlag = true ;
}
void AddVilebook ( int i )
{
if ( setlevel & & setlvlnum = = SL_VILEBETRAYER ) {
Objects [ i ] . _oAnimFrame = 4 ;
}
}
void AddMagicCircle ( int i )
{
Objects [ i ] . _oRndSeed = AdvanceRndSeed ( ) ;
Objects [ i ] . _oPreFlag = true ;
Objects [ i ] . _oVar6 = 0 ;
Objects [ i ] . _oVar5 = 1 ;
}
void AddBrnCross ( int i )
{
Objects [ i ] . _oRndSeed = AdvanceRndSeed ( ) ;
}
void AddPedistal ( int i )
{
Objects [ i ] . _oVar1 = setpc_x ;
Objects [ i ] . _oVar2 = setpc_y ;
Objects [ i ] . _oVar3 = setpc_x + setpc_w ;
Objects [ i ] . _oVar4 = setpc_y + setpc_h ;
Objects [ i ] . _oVar6 = 0 ;
}
void AddStoryBook ( int i )
{
SetRndSeed ( glSeedTbl [ 16 ] ) ;
Objects [ i ] . _oVar1 = GenerateRnd ( 3 ) ;
if ( currlevel = = 4 )
Objects [ i ] . _oVar2 = StoryText [ Objects [ i ] . _oVar1 ] [ 0 ] ;
else if ( currlevel = = 8 )
Objects [ i ] . _oVar2 = StoryText [ Objects [ i ] . _oVar1 ] [ 1 ] ;
else if ( currlevel = = 12 )
Objects [ i ] . _oVar2 = StoryText [ Objects [ i ] . _oVar1 ] [ 2 ] ;
Objects [ i ] . _oVar3 = ( currlevel / 4 ) + 3 * Objects [ i ] . _oVar1 - 1 ;
Objects [ i ] . _oAnimFrame = 5 - 2 * Objects [ i ] . _oVar1 ;
Objects [ i ] . _oVar4 = Objects [ i ] . _oAnimFrame + 1 ;
}
void AddWeaponRack ( int i )
{
if ( ! weaponFlag ) {
Objects [ i ] . _oAnimFlag = 2 ;
Objects [ i ] . _oSelFlag = 0 ;
}
Objects [ i ] . _oRndSeed = AdvanceRndSeed ( ) ;
}
void AddTorturedBody ( int i )
{
Objects [ i ] . _oRndSeed = AdvanceRndSeed ( ) ;
Objects [ i ] . _oAnimFrame = GenerateRnd ( 4 ) + 1 ;
Objects [ i ] . _oPreFlag = true ;
}
void GetRndObjLoc ( int randarea , int * xx , int * yy )
{
if ( randarea = = 0 )
return ;
int tries = 0 ;
while ( true ) {
tries + + ;
if ( tries > 1000 & & randarea > 1 )
randarea - - ;
* xx = GenerateRnd ( MAXDUNX ) ;
* yy = GenerateRnd ( MAXDUNY ) ;
bool failed = false ;
for ( int i = 0 ; i < randarea & & ! failed ; i + + ) {
for ( int j = 0 ; j < randarea & & ! failed ; j + + ) {
failed = ! RndLocOk ( i + * xx , j + * yy ) ;
}
}
if ( ! failed )
break ;
}
}
void AddMushPatch ( )
{
int y ;
int x ;
if ( ActiveObjectCount < MAXOBJECTS ) {
int i = AvailableObjects [ 0 ] ;
GetRndObjLoc ( 5 , & x , & y ) ;
dObject [ x + 1 ] [ y + 1 ] = - ( i + 1 ) ;
dObject [ x + 2 ] [ y + 1 ] = - ( i + 1 ) ;
dObject [ x + 1 ] [ y + 2 ] = - ( i + 1 ) ;
AddObject ( OBJ_MUSHPATCH , { x + 2 , y + 2 } ) ;
}
}
void UpdateObjectLight ( int i , int lightRadius )
{
if ( Objects [ i ] . _oVar1 = = - 1 ) {
return ;
}
bool turnon = false ;
int ox = Objects [ i ] . position . x ;
int oy = Objects [ i ] . position . y ;
int tr = lightRadius + 10 ;
if ( ! DisableLighting ) {
for ( int p = 0 ; p < MAX_PLRS & & ! turnon ; p + + ) {
if ( Players [ p ] . plractive ) {
if ( currlevel = = Players [ p ] . plrlevel ) {
int dx = abs ( Players [ p ] . position . tile . x - ox ) ;
int dy = abs ( Players [ p ] . position . tile . y - oy ) ;
if ( dx < tr & & dy < tr )
turnon = true ;
}
}
}
}
if ( turnon ) {
if ( Objects [ i ] . _oVar1 = = 0 )
Objects [ i ] . _olid = AddLight ( Objects [ i ] . position , lightRadius ) ;
Objects [ i ] . _oVar1 = 1 ;
} else {
if ( Objects [ i ] . _oVar1 = = 1 )
AddUnLight ( Objects [ i ] . _olid ) ;
Objects [ i ] . _oVar1 = 0 ;
}
}
void UpdateCircle ( int i )
{
auto & myPlayer = Players [ MyPlayerId ] ;
if ( myPlayer . position . tile ! = Objects [ i ] . position ) {
if ( Objects [ i ] . _otype = = OBJ_MCIRCLE1 )
Objects [ i ] . _oAnimFrame = 1 ;
if ( Objects [ i ] . _otype = = OBJ_MCIRCLE2 )
Objects [ i ] . _oAnimFrame = 3 ;
Objects [ i ] . _oVar6 = 0 ;
return ;
}
int ox = Objects [ i ] . position . x ;
int oy = Objects [ i ] . position . y ;
if ( Objects [ i ] . _otype = = OBJ_MCIRCLE1 )
Objects [ i ] . _oAnimFrame = 2 ;
if ( Objects [ i ] . _otype = = OBJ_MCIRCLE2 )
Objects [ i ] . _oAnimFrame = 4 ;
if ( ox = = 45 & & oy = = 47 ) {
Objects [ i ] . _oVar6 = 2 ;
} else if ( ox = = 26 & & oy = = 46 ) {
Objects [ i ] . _oVar6 = 1 ;
} else {
Objects [ i ] . _oVar6 = 0 ;
}
if ( ox = = 35 & & oy = = 36 & & Objects [ i ] . _oVar5 = = 3 ) {
Objects [ i ] . _oVar6 = 4 ;
ObjChangeMapResync ( Objects [ i ] . _oVar1 , Objects [ i ] . _oVar2 , Objects [ i ] . _oVar3 , Objects [ i ] . _oVar4 ) ;
if ( Quests [ Q_BETRAYER ] . _qactive = = QUEST_ACTIVE & & Quests [ Q_BETRAYER ] . _qvar1 < = 4 ) // BUGFIX stepping on the circle again will break the quest state (fixed)
Quests [ Q_BETRAYER ] . _qvar1 = 4 ;
AddMissile ( myPlayer . position . tile , { 35 , 46 } , myPlayer . _pdir , MIS_RNDTELEPORT , TARGET_MONSTERS , MyPlayerId , 0 , 0 ) ;
LastMouseButtonAction = MouseActionType : : None ;
sgbMouseDown = CLICK_NONE ;
ClrPlrPath ( myPlayer ) ;
StartStand ( MyPlayerId , DIR_S ) ;
}
}
void ObjectStopAnim ( int i )
{
if ( Objects [ i ] . _oAnimFrame = = Objects [ i ] . _oAnimLen ) {
Objects [ i ] . _oAnimCnt = 0 ;
Objects [ i ] . _oAnimDelay = 1000 ;
}
}
void UpdateDoor ( int i )
{
if ( Objects [ i ] . _oVar4 = = 0 ) {
Objects [ i ] . _oSelFlag = 3 ;
Objects [ i ] . _oMissFlag = false ;
return ;
}
int dx = Objects [ i ] . position . x ;
int dy = Objects [ i ] . position . y ;
bool dok = dMonster [ dx ] [ dy ] = = 0 ;
dok = dok & & dItem [ dx ] [ dy ] = = 0 ;
dok = dok & & dDead [ dx ] [ dy ] = = 0 ;
dok = dok & & dPlayer [ dx ] [ dy ] = = 0 ;
Objects [ i ] . _oSelFlag = 2 ;
Objects [ i ] . _oVar4 = dok ? 1 : 2 ;
Objects [ i ] . _oMissFlag = true ;
}
void UpdateSarcoffagus ( int i )
{
if ( Objects [ i ] . _oAnimFrame = = Objects [ i ] . _oAnimLen )
Objects [ i ] . _oAnimFlag = 0 ;
}
void ActivateTrapLine ( int ttype , int tid )
{
for ( int i = 0 ; i < ActiveObjectCount ; i + + ) {
int oi = ActiveObjects [ i ] ;
if ( Objects [ oi ] . _otype = = ttype & & Objects [ oi ] . _oVar1 = = tid ) {
Objects [ oi ] . _oVar4 = 1 ;
Objects [ oi ] . _oAnimFlag = 1 ;
Objects [ oi ] . _oAnimDelay = 1 ;
Objects [ oi ] . _olid = AddLight ( Objects [ oi ] . position , 1 ) ;
}
}
}
void UpdateFlameTrap ( int i )
{
if ( Objects [ i ] . _oVar2 ! = 0 ) {
if ( Objects [ i ] . _oVar4 ! = 0 ) {
Objects [ i ] . _oAnimFrame - - ;
if ( Objects [ i ] . _oAnimFrame = = 1 ) {
Objects [ i ] . _oVar4 = 0 ;
AddUnLight ( Objects [ i ] . _olid ) ;
} else if ( Objects [ i ] . _oAnimFrame < = 4 ) {
ChangeLightRadius ( Objects [ i ] . _olid , Objects [ i ] . _oAnimFrame ) ;
}
}
} else if ( Objects [ i ] . _oVar4 = = 0 ) {
if ( Objects [ i ] . _oVar3 = = 2 ) {
int x = Objects [ i ] . position . x - 2 ;
int y = Objects [ i ] . position . y ;
for ( int j = 0 ; j < 5 ; j + + ) {
if ( dPlayer [ x ] [ y ] ! = 0 | | dMonster [ x ] [ y ] ! = 0 )
Objects [ i ] . _oVar4 = 1 ;
x + + ;
}
} else {
int x = Objects [ i ] . position . x ;
int y = Objects [ i ] . position . y - 2 ;
for ( int k = 0 ; k < 5 ; k + + ) {
if ( dPlayer [ x ] [ y ] ! = 0 | | dMonster [ x ] [ y ] ! = 0 )
Objects [ i ] . _oVar4 = 1 ;
y + + ;
}
}
if ( Objects [ i ] . _oVar4 ! = 0 )
ActivateTrapLine ( Objects [ i ] . _otype , Objects [ i ] . _oVar1 ) ;
} else {
int damage [ 4 ] = { 6 , 8 , 10 , 12 } ;
int mindam = damage [ leveltype - 1 ] ;
int maxdam = mindam * 2 ;
int x = Objects [ i ] . position . x ;
int y = Objects [ i ] . position . y ;
if ( dMonster [ x ] [ y ] > 0 )
MonsterTrapHit ( dMonster [ x ] [ y ] - 1 , mindam / 2 , maxdam / 2 , 0 , MIS_FIREWALLC , false ) ;
if ( dPlayer [ x ] [ y ] > 0 ) {
bool unused ;
PlayerMHit ( dPlayer [ x ] [ y ] - 1 , nullptr , 0 , mindam , maxdam , MIS_FIREWALLC , false , 0 , & unused ) ;
}
if ( Objects [ i ] . _oAnimFrame = = Objects [ i ] . _oAnimLen )
Objects [ i ] . _oAnimFrame = 11 ;
if ( Objects [ i ] . _oAnimFrame < = 5 )
ChangeLightRadius ( Objects [ i ] . _olid , Objects [ i ] . _oAnimFrame ) ;
}
}
void UpdateBurningCrossDamage ( int i )
{
int damage [ 4 ] = { 6 , 8 , 10 , 12 } ;
auto & myPlayer = Players [ MyPlayerId ] ;
if ( myPlayer . _pmode = = PM_DEATH )
return ;
int8_t fireResist = myPlayer . _pFireResist ;
if ( fireResist > 0 )
damage [ leveltype - 1 ] - = fireResist * damage [ leveltype - 1 ] / 100 ;
if ( myPlayer . position . tile . x ! = Objects [ i ] . position . x | | myPlayer . position . tile . y ! = Objects [ i ] . position . y - 1 )
return ;
ApplyPlrDamage ( MyPlayerId , 0 , 0 , damage [ leveltype - 1 ] ) ;
if ( myPlayer . _pHitPoints > > 6 > 0 ) {
myPlayer . Say ( HeroSpeech : : Argh ) ;
}
}
void ObjSetMini ( Point position , int v )
{
MegaTile mega = pMegaTiles [ v - 1 ] ;
Point megaOrigin = position * 2 + Displacement { 16 , 16 } ;
ObjSetMicro ( megaOrigin , SDL_SwapLE16 ( mega . micro1 ) + 1 ) ;
ObjSetMicro ( megaOrigin + DIR_SE , SDL_SwapLE16 ( mega . micro2 ) + 1 ) ;
ObjSetMicro ( megaOrigin + DIR_SW , SDL_SwapLE16 ( mega . micro3 ) + 1 ) ;
ObjSetMicro ( megaOrigin + DIR_S , SDL_SwapLE16 ( mega . micro4 ) + 1 ) ;
}
void ObjL1Special ( int x1 , int y1 , int x2 , int y2 )
{
for ( int i = y1 ; i < = y2 ; + + i ) {
for ( int j = x1 ; j < = x2 ; + + j ) {
dSpecial [ j ] [ i ] = 0 ;
if ( dPiece [ j ] [ i ] = = 12 )
dSpecial [ j ] [ i ] = 1 ;
if ( dPiece [ j ] [ i ] = = 11 )
dSpecial [ j ] [ i ] = 2 ;
if ( dPiece [ j ] [ i ] = = 71 )
dSpecial [ j ] [ i ] = 1 ;
if ( dPiece [ j ] [ i ] = = 253 )
dSpecial [ j ] [ i ] = 3 ;
if ( dPiece [ j ] [ i ] = = 267 )
dSpecial [ j ] [ i ] = 6 ;
if ( dPiece [ j ] [ i ] = = 259 )
dSpecial [ j ] [ i ] = 5 ;
if ( dPiece [ j ] [ i ] = = 249 )
dSpecial [ j ] [ i ] = 2 ;
if ( dPiece [ j ] [ i ] = = 325 )
dSpecial [ j ] [ i ] = 2 ;
if ( dPiece [ j ] [ i ] = = 321 )
dSpecial [ j ] [ i ] = 1 ;
if ( dPiece [ j ] [ i ] = = 255 )
dSpecial [ j ] [ i ] = 4 ;
if ( dPiece [ j ] [ i ] = = 211 )
dSpecial [ j ] [ i ] = 1 ;
if ( dPiece [ j ] [ i ] = = 344 )
dSpecial [ j ] [ i ] = 2 ;
if ( dPiece [ j ] [ i ] = = 341 )
dSpecial [ j ] [ i ] = 1 ;
if ( dPiece [ j ] [ i ] = = 331 )
dSpecial [ j ] [ i ] = 2 ;
if ( dPiece [ j ] [ i ] = = 418 )
dSpecial [ j ] [ i ] = 1 ;
if ( dPiece [ j ] [ i ] = = 421 )
dSpecial [ j ] [ i ] = 2 ;
}
}
}
void ObjL2Special ( int x1 , int y1 , int x2 , int y2 )
{
for ( int j = y1 ; j < = y2 ; j + + ) {
for ( int i = x1 ; i < = x2 ; i + + ) {
dSpecial [ i ] [ j ] = 0 ;
if ( dPiece [ i ] [ j ] = = 541 )
dSpecial [ i ] [ j ] = 5 ;
if ( dPiece [ i ] [ j ] = = 178 )
dSpecial [ i ] [ j ] = 5 ;
if ( dPiece [ i ] [ j ] = = 551 )
dSpecial [ i ] [ j ] = 5 ;
if ( dPiece [ i ] [ j ] = = 542 )
dSpecial [ i ] [ j ] = 6 ;
if ( dPiece [ i ] [ j ] = = 553 )
dSpecial [ i ] [ j ] = 6 ;
}
}
for ( int j = y1 ; j < = y2 ; j + + ) {
for ( int i = x1 ; i < = x2 ; i + + ) {
if ( dPiece [ i ] [ j ] = = 132 ) {
dSpecial [ i ] [ j + 1 ] = 2 ;
dSpecial [ i ] [ j + 2 ] = 1 ;
}
if ( dPiece [ i ] [ j ] = = 135 | | dPiece [ i ] [ j ] = = 139 ) {
dSpecial [ i + 1 ] [ j ] = 3 ;
dSpecial [ i + 2 ] [ j ] = 4 ;
}
}
}
}
void SetDoorPiece ( Point position )
{
int pn = dPiece [ position . x ] [ position . y ] - 1 ;
uint16_t * piece = & pLevelPieces [ 10 * pn + 8 ] ;
dpiece_defs_map_2 [ position . x ] [ position . y ] . mt [ 0 ] = SDL_SwapLE16 ( piece [ 0 ] ) ;
dpiece_defs_map_2 [ position . x ] [ position . y ] . mt [ 1 ] = SDL_SwapLE16 ( piece [ 1 ] ) ;
}
void DoorSet ( Point position , bool isLeftDoor )
{
int pn = dPiece [ position . x ] [ position . y ] ;
if ( currlevel < 17 ) {
switch ( pn ) {
case 43 :
ObjSetMicro ( position , 392 ) ;
break ;
case 45 :
ObjSetMicro ( position , 394 ) ;
break ;
case 50 :
ObjSetMicro ( position , isLeftDoor ? 411 : 412 ) ;
break ;
case 54 :
ObjSetMicro ( position , 397 ) ;
break ;
case 55 :
ObjSetMicro ( position , 398 ) ;
break ;
case 61 :
ObjSetMicro ( position , 399 ) ;
break ;
case 67 :
ObjSetMicro ( position , 400 ) ;
break ;
case 68 :
ObjSetMicro ( position , 401 ) ;
break ;
case 69 :
ObjSetMicro ( position , 403 ) ;
break ;
case 70 :
ObjSetMicro ( position , 404 ) ;
break ;
case 72 :
ObjSetMicro ( position , 406 ) ;
break ;
case 212 :
ObjSetMicro ( position , 407 ) ;
break ;
case 354 :
ObjSetMicro ( position , 409 ) ;
break ;
case 355 :
ObjSetMicro ( position , 410 ) ;
break ;
case 411 :
case 412 :
ObjSetMicro ( position , 396 ) ;
break ;
}
} else {
switch ( pn ) {
case 75 :
ObjSetMicro ( position , 204 ) ;
break ;
case 79 :
ObjSetMicro ( position , 208 ) ;
break ;
case 86 :
ObjSetMicro ( position , isLeftDoor ? 232 : 234 ) ;
break ;
case 91 :
ObjSetMicro ( position , 215 ) ;
break ;
case 93 :
ObjSetMicro ( position , 218 ) ;
break ;
case 99 :
ObjSetMicro ( position , 220 ) ;
break ;
case 111 :
ObjSetMicro ( position , 222 ) ;
break ;
case 113 :
ObjSetMicro ( position , 224 ) ;
break ;
case 115 :
ObjSetMicro ( position , 226 ) ;
break ;
case 117 :
ObjSetMicro ( position , 228 ) ;
break ;
case 119 :
ObjSetMicro ( position , 230 ) ;
break ;
case 232 :
case 234 :
ObjSetMicro ( position , 212 ) ;
break ;
}
}
}
/**
* @ brief Checks if an open door can be closed
*
* In order to be able to close a door the space where the closed door would be must be free of bodies , monsters , and items
*
* @ param doorPos Map tile where the door is in its closed position
* @ return true if the door is free to be closed , false if anything is blocking it
*/
inline bool IsDoorClear ( const Point & doorPosition )
{
return dDead [ doorPosition . x ] [ doorPosition . y ] = = 0
& & dMonster [ doorPosition . x ] [ doorPosition . y ] = = 0
& & dItem [ doorPosition . x ] [ doorPosition . y ] = = 0 ;
}
void OperateL1RDoor ( int pnum , int oi , bool sendflag )
{
ObjectStruct & door = Objects [ oi ] ;
if ( door . _oVar4 = = 2 ) {
if ( ! deltaload )
PlaySfxLoc ( IS_DOORCLOS , door . position ) ;
return ;
}
if ( door . _oVar4 = = 0 ) {
if ( pnum = = MyPlayerId & & sendflag )
NetSendCmdParam1 ( true , CMD_OPENDOOR , oi ) ;
if ( currlevel < 21 ) {
if ( ! deltaload )
PlaySfxLoc ( IS_DOOROPEN , door . position ) ;
ObjSetMicro ( door . position , 395 ) ;
} else {
if ( ! deltaload )
PlaySfxLoc ( IS_CROPEN , door . position ) ;
ObjSetMicro ( door . position , 209 ) ;
}
if ( currlevel < 17 ) {
dSpecial [ door . position . x ] [ door . position . y ] = 8 ;
} else {
dSpecial [ door . position . x ] [ door . position . y ] = 2 ;
}
SetDoorPiece ( door . position + Direction : : DIR_NE ) ;
door . _oAnimFrame + = 2 ;
door . _oPreFlag = true ;
DoorSet ( door . position + Direction : : DIR_NW , false ) ;
door . _oVar4 = 1 ;
door . _oSelFlag = 2 ;
RedoPlayerVision ( ) ;
return ;
}
if ( currlevel < 21 ) {
if ( ! deltaload )
PlaySfxLoc ( IS_DOORCLOS , door . position ) ;
} else {
if ( ! deltaload )
PlaySfxLoc ( IS_CRCLOS , door . position ) ;
}
if ( ! deltaload & & IsDoorClear ( door . position ) ) {
if ( pnum = = MyPlayerId & & sendflag )
NetSendCmdParam1 ( true , CMD_CLOSEDOOR , oi ) ;
door . _oVar4 = 0 ;
door . _oSelFlag = 3 ;
ObjSetMicro ( door . position , door . _oVar1 ) ;
if ( currlevel < 17 ) {
if ( door . _oVar2 ! = 50 ) {
ObjSetMicro ( door . position + Direction : : DIR_NW , door . _oVar2 ) ;
} else {
if ( dPiece [ door . position . x - 1 ] [ door . position . y ] = = 396 )
ObjSetMicro ( door . position + Direction : : DIR_NW , 411 ) ;
else
ObjSetMicro ( door . position + Direction : : DIR_NW , 50 ) ;
}
} else {
if ( door . _oVar2 ! = 86 ) {
ObjSetMicro ( door . position + Direction : : DIR_NW , door . _oVar2 ) ;
} else {
if ( dPiece [ door . position . x - 1 ] [ door . position . y ] = = 210 )
ObjSetMicro ( door . position + Direction : : DIR_NW , 232 ) ;
else
ObjSetMicro ( door . position + Direction : : DIR_NW , 86 ) ;
}
}
dSpecial [ door . position . x ] [ door . position . y ] = 0 ;
door . _oAnimFrame - = 2 ;
door . _oPreFlag = false ;
RedoPlayerVision ( ) ;
} else {
door . _oVar4 = 2 ;
}
}
void OperateL1LDoor ( int pnum , int oi , bool sendflag )
{
ObjectStruct & door = Objects [ oi ] ;
if ( door . _oVar4 = = 2 ) {
if ( ! deltaload )
PlaySfxLoc ( IS_DOORCLOS , door . position ) ;
return ;
}
if ( door . _oVar4 = = 0 ) {
if ( pnum = = MyPlayerId & & sendflag )
NetSendCmdParam1 ( true , CMD_OPENDOOR , oi ) ;
if ( currlevel < 21 ) {
if ( ! deltaload )
PlaySfxLoc ( IS_DOOROPEN , door . position ) ;
if ( door . _oVar1 = = 214 )
ObjSetMicro ( door . position , 408 ) ;
else
ObjSetMicro ( door . position , 393 ) ;
} else {
if ( ! deltaload )
PlaySfxLoc ( IS_CROPEN , door . position ) ;
ObjSetMicro ( door . position , 206 ) ;
}
if ( currlevel < 17 ) {
dSpecial [ door . position . x ] [ door . position . y ] = 7 ;
} else {
dSpecial [ door . position . x ] [ door . position . y ] = 1 ;
}
SetDoorPiece ( door . position + Direction : : DIR_NW ) ;
door . _oAnimFrame + = 2 ;
door . _oPreFlag = true ;
DoorSet ( door . position + Direction : : DIR_NE , true ) ;
door . _oVar4 = 1 ;
door . _oSelFlag = 2 ;
RedoPlayerVision ( ) ;
return ;
}
if ( currlevel < 21 ) {
if ( ! deltaload )
PlaySfxLoc ( IS_DOORCLOS , door . position ) ;
} else {
if ( ! deltaload )
PlaySfxLoc ( IS_CRCLOS , door . position ) ;
}
if ( IsDoorClear ( door . position ) ) {
if ( pnum = = MyPlayerId & & sendflag )
NetSendCmdParam1 ( true , CMD_CLOSEDOOR , oi ) ;
door . _oVar4 = 0 ;
door . _oSelFlag = 3 ;
ObjSetMicro ( door . position , door . _oVar1 ) ;
if ( currlevel < 17 ) {
if ( door . _oVar2 ! = 50 ) {
ObjSetMicro ( door . position + Direction : : DIR_NE , door . _oVar2 ) ;
} else {
if ( dPiece [ door . position . x ] [ door . position . y - 1 ] = = 396 )
ObjSetMicro ( door . position + Direction : : DIR_NE , 412 ) ;
else
ObjSetMicro ( door . position + Direction : : DIR_NE , 50 ) ;
}
} else {
if ( door . _oVar2 ! = 86 ) {
ObjSetMicro ( door . position + Direction : : DIR_NE , door . _oVar2 ) ;
} else {
if ( dPiece [ door . position . x ] [ door . position . y - 1 ] = = 210 )
ObjSetMicro ( door . position + Direction : : DIR_NE , 234 ) ;
else
ObjSetMicro ( door . position + Direction : : DIR_NE , 86 ) ;
}
}
dSpecial [ door . position . x ] [ door . position . y ] = 0 ;
door . _oAnimFrame - = 2 ;
door . _oPreFlag = false ;
RedoPlayerVision ( ) ;
} else {
door . _oVar4 = 2 ;
}
}
void OperateL2RDoor ( int pnum , int oi , bool sendflag )
{
ObjectStruct & door = Objects [ oi ] ;
if ( door . _oVar4 = = 2 ) {
if ( ! deltaload )
PlaySfxLoc ( IS_DOORCLOS , door . position ) ;
return ;
}
if ( door . _oVar4 = = 0 ) {
if ( pnum = = MyPlayerId & & sendflag )
NetSendCmdParam1 ( true , CMD_OPENDOOR , oi ) ;
if ( ! deltaload )
PlaySfxLoc ( IS_DOOROPEN , door . position ) ;
ObjSetMicro ( door . position , 17 ) ;
dSpecial [ door . position . x ] [ door . position . y ] = 6 ;
door . _oAnimFrame + = 2 ;
door . _oPreFlag = true ;
door . _oVar4 = 1 ;
door . _oSelFlag = 2 ;
RedoPlayerVision ( ) ;
return ;
}
if ( ! deltaload )
PlaySfxLoc ( IS_DOORCLOS , door . position ) ;
if ( IsDoorClear ( door . position ) ) {
if ( pnum = = MyPlayerId & & sendflag )
NetSendCmdParam1 ( true , CMD_CLOSEDOOR , oi ) ;
door . _oVar4 = 0 ;
door . _oSelFlag = 3 ;
ObjSetMicro ( door . position , 540 ) ;
dSpecial [ door . position . x ] [ door . position . y ] = 0 ;
door . _oAnimFrame - = 2 ;
door . _oPreFlag = false ;
RedoPlayerVision ( ) ;
} else {
door . _oVar4 = 2 ;
}
}
void OperateL2LDoor ( int pnum , int oi , bool sendflag )
{
ObjectStruct & door = Objects [ oi ] ;
if ( door . _oVar4 = = 2 ) {
if ( ! deltaload )
PlaySfxLoc ( IS_DOORCLOS , door . position ) ;
return ;
}
if ( door . _oVar4 = = 0 ) {
if ( pnum = = MyPlayerId & & sendflag )
NetSendCmdParam1 ( true , CMD_OPENDOOR , oi ) ;
if ( ! deltaload )
PlaySfxLoc ( IS_DOOROPEN , door . position ) ;
ObjSetMicro ( door . position , 13 ) ;
dSpecial [ door . position . x ] [ door . position . y ] = 5 ;
door . _oAnimFrame + = 2 ;
door . _oPreFlag = true ;
door . _oVar4 = 1 ;
door . _oSelFlag = 2 ;
RedoPlayerVision ( ) ;
return ;
}
if ( ! deltaload )
PlaySfxLoc ( IS_DOORCLOS , door . position ) ;
if ( IsDoorClear ( door . position ) ) {
if ( pnum = = MyPlayerId & & sendflag )
NetSendCmdParam1 ( true , CMD_CLOSEDOOR , oi ) ;
door . _oVar4 = 0 ;
door . _oSelFlag = 3 ;
ObjSetMicro ( door . position , 538 ) ;
dSpecial [ door . position . x ] [ door . position . y ] = 0 ;
door . _oAnimFrame - = 2 ;
door . _oPreFlag = false ;
RedoPlayerVision ( ) ;
} else {
door . _oVar4 = 2 ;
}
}
void OperateL3RDoor ( int pnum , int oi , bool sendflag )
{
ObjectStruct & door = Objects [ oi ] ;
if ( door . _oVar4 = = 2 ) {
if ( ! deltaload )
PlaySfxLoc ( IS_DOORCLOS , door . position ) ;
return ;
}
if ( door . _oVar4 = = 0 ) {
if ( pnum = = MyPlayerId & & sendflag )
NetSendCmdParam1 ( true , CMD_OPENDOOR , oi ) ;
if ( ! deltaload )
PlaySfxLoc ( IS_DOOROPEN , door . position ) ;
ObjSetMicro ( door . position , 541 ) ;
door . _oAnimFrame + = 2 ;
door . _oPreFlag = true ;
door . _oVar4 = 1 ;
door . _oSelFlag = 2 ;
RedoPlayerVision ( ) ;
return ;
}
if ( ! deltaload )
PlaySfxLoc ( IS_DOORCLOS , door . position ) ;
if ( IsDoorClear ( door . position ) ) {
if ( pnum = = MyPlayerId & & sendflag )
NetSendCmdParam1 ( true , CMD_CLOSEDOOR , oi ) ;
door . _oVar4 = 0 ;
door . _oSelFlag = 3 ;
ObjSetMicro ( door . position , 534 ) ;
door . _oAnimFrame - = 2 ;
door . _oPreFlag = false ;
RedoPlayerVision ( ) ;
} else {
door . _oVar4 = 2 ;
}
}
void OperateL3LDoor ( int pnum , int oi , bool sendflag )
{
ObjectStruct & door = Objects [ oi ] ;
if ( door . _oVar4 = = 2 ) {
if ( ! deltaload )
PlaySfxLoc ( IS_DOORCLOS , door . position ) ;
return ;
}
if ( door . _oVar4 = = 0 ) {
if ( pnum = = MyPlayerId & & sendflag )
NetSendCmdParam1 ( true , CMD_OPENDOOR , oi ) ;
if ( ! deltaload )
PlaySfxLoc ( IS_DOOROPEN , door . position ) ;
ObjSetMicro ( door . position , 538 ) ;
door . _oAnimFrame + = 2 ;
door . _oPreFlag = true ;
door . _oVar4 = 1 ;
door . _oSelFlag = 2 ;
RedoPlayerVision ( ) ;
return ;
}
if ( ! deltaload )
PlaySfxLoc ( IS_DOORCLOS , door . position ) ;
if ( IsDoorClear ( door . position ) ) {
if ( pnum = = MyPlayerId & & sendflag )
NetSendCmdParam1 ( true , CMD_CLOSEDOOR , oi ) ;
door . _oVar4 = 0 ;
door . _oSelFlag = 3 ;
ObjSetMicro ( door . position , 531 ) ;
door . _oAnimFrame - = 2 ;
door . _oPreFlag = false ;
RedoPlayerVision ( ) ;
} else {
door . _oVar4 = 2 ;
}
}
void OperateL1Door ( int pnum , int i , bool sendflag )
{
int dpx = abs ( Objects [ i ] . position . x - Players [ pnum ] . position . tile . x ) ;
int dpy = abs ( Objects [ i ] . position . y - Players [ pnum ] . position . tile . y ) ;
if ( dpx = = 1 & & dpy < = 1 & & Objects [ i ] . _otype = = OBJ_L1LDOOR )
OperateL1LDoor ( pnum , i , sendflag ) ;
if ( dpx < = 1 & & dpy = = 1 & & Objects [ i ] . _otype = = OBJ_L1RDOOR )
OperateL1RDoor ( pnum , i , sendflag ) ;
}
void OperateLever ( int pnum , int i )
{
if ( Objects [ i ] . _oSelFlag = = 0 ) {
return ;
}
if ( ! deltaload )
PlaySfxLoc ( IS_LEVER , Objects [ i ] . position ) ;
Objects [ i ] . _oSelFlag = 0 ;
Objects [ i ] . _oAnimFrame + + ;
bool mapflag = true ;
if ( currlevel = = 16 ) {
for ( int j = 0 ; j < ActiveObjectCount ; j + + ) {
int oi = ActiveObjects [ j ] ;
if ( Objects [ oi ] . _otype = = OBJ_SWITCHSKL
& & Objects [ i ] . _oVar8 = = Objects [ oi ] . _oVar8
& & Objects [ oi ] . _oSelFlag ! = 0 ) {
mapflag = false ;
}
}
}
if ( currlevel = = 24 ) {
OperateNakrulLever ( ) ;
IsUberLeverActivated = true ;
mapflag = false ;
Quests [ Q_NAKRUL ] . _qactive = QUEST_DONE ;
}
if ( mapflag )
ObjChangeMap ( Objects [ i ] . _oVar1 , Objects [ i ] . _oVar2 , Objects [ i ] . _oVar3 , Objects [ i ] . _oVar4 ) ;
if ( pnum = = MyPlayerId )
NetSendCmdParam1 ( false , CMD_OPERATEOBJ , i ) ;
}
void OperateBook ( int pnum , int i )
{
int dx ;
int dy ;
auto & player = Players [ pnum ] ;
if ( Objects [ i ] . _oSelFlag = = 0 )
return ;
if ( setlevel & & setlvlnum = = SL_VILEBETRAYER ) {
bool doAddMissile = false ;
bool missileAdded = false ;
for ( int j = 0 ; j < ActiveObjectCount ; j + + ) {
int oi = ActiveObjects [ j ] ;
int otype = Objects [ oi ] . _otype ;
if ( otype = = OBJ_MCIRCLE2 & & Objects [ oi ] . _oVar6 = = 1 ) {
dx = 27 ;
dy = 29 ;
Objects [ oi ] . _oVar6 = 4 ;
doAddMissile = true ;
}
if ( otype = = OBJ_MCIRCLE2 & & Objects [ oi ] . _oVar6 = = 2 ) {
dx = 43 ;
dy = 29 ;
Objects [ oi ] . _oVar6 = 4 ;
doAddMissile = true ;
}
if ( doAddMissile ) {
Objects [ dObject [ 35 ] [ 36 ] - 1 ] . _oVar5 + + ;
AddMissile ( player . position . tile , { dx , dy } , player . _pdir , MIS_RNDTELEPORT , TARGET_MONSTERS , pnum , 0 , 0 ) ;
missileAdded = true ;
doAddMissile = false ;
}
}
if ( ! missileAdded )
return ;
}
Objects [ i ] . _oSelFlag = 0 ;
Objects [ i ] . _oAnimFrame + + ;
if ( ! setlevel )
return ;
if ( setlvlnum = = SL_BONECHAMB ) {
player . _pMemSpells | = GetSpellBitmask ( SPL_GUARDIAN ) ;
if ( player . _pSplLvl [ SPL_GUARDIAN ] < MAX_SPELL_LEVEL )
player . _pSplLvl [ SPL_GUARDIAN ] + + ;
Quests [ Q_SCHAMB ] . _qactive = QUEST_DONE ;
if ( ! deltaload )
PlaySfxLoc ( IS_QUESTDN , Objects [ i ] . position ) ;
InitDiabloMsg ( EMSG_BONECHAMB ) ;
AddMissile (
player . position . tile ,
Objects [ i ] . position + Displacement { - 2 , - 4 } ,
player . _pdir ,
MIS_GUARDIAN ,
TARGET_MONSTERS ,
pnum ,
0 ,
0 ) ;
}
if ( setlvlnum = = SL_VILEBETRAYER ) {
ObjChangeMapResync (
Objects [ i ] . _oVar1 ,
Objects [ i ] . _oVar2 ,
Objects [ i ] . _oVar3 ,
Objects [ i ] . _oVar4 ) ;
for ( int j = 0 ; j < ActiveObjectCount ; j + + )
SyncObjectAnim ( Objects [ ActiveObjects [ j ] ] ) ;
}
}
void OperateBookLever ( int pnum , int i )
{
int x = 2 * setpc_x + 16 ;
int y = 2 * setpc_y + 16 ;
if ( ActiveItemCount > = MAXITEMS ) {
return ;
}
if ( Objects [ i ] . _oSelFlag ! = 0 & & ! qtextflag ) {
if ( Objects [ i ] . _otype = = OBJ_BLINDBOOK & & Quests [ Q_BLIND ] . _qvar1 = = 0 ) {
Quests [ Q_BLIND ] . _qactive = QUEST_ACTIVE ;
Quests [ Q_BLIND ] . _qlog = true ;
Quests [ Q_BLIND ] . _qvar1 = 1 ;
}
if ( Objects [ i ] . _otype = = OBJ_BLOODBOOK & & Quests [ Q_BLOOD ] . _qvar1 = = 0 ) {
Quests [ Q_BLOOD ] . _qactive = QUEST_ACTIVE ;
Quests [ Q_BLOOD ] . _qlog = true ;
Quests [ Q_BLOOD ] . _qvar1 = 1 ;
SpawnQuestItem ( IDI_BLDSTONE , { 2 * setpc_x + 25 , 2 * setpc_y + 33 } , 0 , 1 ) ;
}
Objects [ i ] . _otype = Objects [ i ] . _otype ;
if ( Objects [ i ] . _otype = = OBJ_STEELTOME & & Quests [ Q_WARLORD ] . _qvar1 = = 0 ) {
Quests [ Q_WARLORD ] . _qactive = QUEST_ACTIVE ;
Quests [ Q_WARLORD ] . _qlog = true ;
Quests [ Q_WARLORD ] . _qvar1 = 1 ;
}
if ( Objects [ i ] . _oAnimFrame ! = Objects [ i ] . _oVar6 ) {
if ( Objects [ i ] . _otype ! = OBJ_BLOODBOOK )
ObjChangeMap ( Objects [ i ] . _oVar1 , Objects [ i ] . _oVar2 , Objects [ i ] . _oVar3 , Objects [ i ] . _oVar4 ) ;
if ( Objects [ i ] . _otype = = OBJ_BLINDBOOK ) {
SpawnUnique ( UITEM_OPTAMULET , Point { x , y } + Displacement { 5 , 5 } ) ;
auto tren = TransVal ;
TransVal = 9 ;
DRLG_MRectTrans ( Objects [ i ] . _oVar1 , Objects [ i ] . _oVar2 , Objects [ i ] . _oVar3 , Objects [ i ] . _oVar4 ) ;
TransVal = tren ;
}
}
Objects [ i ] . _oAnimFrame = Objects [ i ] . _oVar6 ;
InitQTextMsg ( Objects [ i ] . bookMessage ) ;
if ( pnum = = MyPlayerId )
NetSendCmdParam1 ( false , CMD_OPERATEOBJ , i ) ;
}
}
void OperateSChambBk ( int i )
{
if ( Objects [ i ] . _oSelFlag = = 0 | | qtextflag ) {
return ;
}
if ( Objects [ i ] . _oAnimFrame ! = Objects [ i ] . _oVar6 ) {
ObjChangeMapResync ( Objects [ i ] . _oVar1 , Objects [ i ] . _oVar2 , Objects [ i ] . _oVar3 , Objects [ i ] . _oVar4 ) ;
for ( int j = 0 ; j < ActiveObjectCount ; j + + )
SyncObjectAnim ( Objects [ ActiveObjects [ j ] ] ) ;
}
Objects [ i ] . _oAnimFrame = Objects [ i ] . _oVar6 ;
if ( Quests [ Q_SCHAMB ] . _qactive = = QUEST_INIT ) {
Quests [ Q_SCHAMB ] . _qactive = QUEST_ACTIVE ;
Quests [ Q_SCHAMB ] . _qlog = true ;
}
_speech_id textdef ;
switch ( Players [ MyPlayerId ] . _pClass ) {
case HeroClass : : Warrior :
textdef = TEXT_BONER ;
break ;
case HeroClass : : Rogue :
textdef = TEXT_RBONER ;
break ;
case HeroClass : : Sorcerer :
textdef = TEXT_MBONER ;
break ;
case HeroClass : : Monk :
textdef = TEXT_HBONER ;
break ;
case HeroClass : : Bard :
textdef = TEXT_BBONER ;
break ;
case HeroClass : : Barbarian :
textdef = TEXT_BONER ;
break ;
}
Quests [ Q_SCHAMB ] . _qmsg = textdef ;
InitQTextMsg ( textdef ) ;
}
void OperateChest ( int pnum , int i , bool sendmsg )
{
if ( Objects [ i ] . _oSelFlag = = 0 ) {
return ;
}
if ( ! deltaload )
PlaySfxLoc ( IS_CHEST , Objects [ i ] . position ) ;
Objects [ i ] . _oSelFlag = 0 ;
Objects [ i ] . _oAnimFrame + = 2 ;
if ( deltaload ) {
return ;
}
SetRndSeed ( Objects [ i ] . _oRndSeed ) ;
if ( setlevel ) {
for ( int j = 0 ; j < Objects [ i ] . _oVar1 ; j + + ) {
CreateRndItem ( Objects [ i ] . position , true , sendmsg , false ) ;
}
} else {
for ( int j = 0 ; j < Objects [ i ] . _oVar1 ; j + + ) {
if ( Objects [ i ] . _oVar2 ! = 0 )
CreateRndItem ( Objects [ i ] . position , false , sendmsg , false ) ;
else
CreateRndUseful ( Objects [ i ] . position , sendmsg ) ;
}
}
if ( Objects [ i ] . _oTrapFlag & & Objects [ i ] . _otype > = OBJ_TCHEST1 & & Objects [ i ] . _otype < = OBJ_TCHEST3 ) {
auto & player = Players [ pnum ] ;
Direction mdir = GetDirection ( Objects [ i ] . position , player . position . tile ) ;
missile_id mtype ;
switch ( Objects [ i ] . _oVar4 ) {
case 0 :
mtype = MIS_ARROW ;
break ;
case 1 :
mtype = MIS_FARROW ;
break ;
case 2 :
mtype = MIS_NOVA ;
break ;
case 3 :
mtype = MIS_FIRERING ;
break ;
case 4 :
mtype = MIS_STEALPOTS ;
break ;
case 5 :
mtype = MIS_MANATRAP ;
break ;
default :
mtype = MIS_ARROW ;
}
AddMissile ( Objects [ i ] . position , player . position . tile , mdir , mtype , TARGET_PLAYERS , - 1 , 0 , 0 ) ;
Objects [ i ] . _oTrapFlag = false ;
}
if ( pnum = = MyPlayerId )
NetSendCmdParam2 ( false , CMD_PLROPOBJ , pnum , i ) ;
}
void OperateMushPatch ( int pnum , int i )
{
if ( ActiveItemCount > = MAXITEMS ) {
return ;
}
if ( Quests [ Q_MUSHROOM ] . _qactive ! = QUEST_ACTIVE ) {
if ( ! deltaload & & pnum = = MyPlayerId ) {
Players [ MyPlayerId ] . Say ( HeroSpeech : : ICantUseThisYet ) ;
}
return ;
}
if ( Objects [ i ] . _oSelFlag ! = 0 ) {
if ( ! deltaload )
PlaySfxLoc ( IS_CHEST , Objects [ i ] . position ) ;
Objects [ i ] . _oSelFlag = 0 ;
Objects [ i ] . _oAnimFrame + + ;
if ( ! deltaload ) {
Point pos = GetSuperItemLoc ( Objects [ i ] . position ) ;
SpawnQuestItem ( IDI_MUSHROOM , pos , 0 , 0 ) ;
Quests [ Q_MUSHROOM ] . _qvar1 = QS_MUSHSPAWNED ;
}
}
}
void OperateInnSignChest ( int pnum , int i )
{
if ( ActiveItemCount > = MAXITEMS ) {
return ;
}
if ( Quests [ Q_LTBANNER ] . _qvar1 ! = 2 ) {
if ( ! deltaload & & pnum = = MyPlayerId ) {
Players [ MyPlayerId ] . Say ( HeroSpeech : : ICantOpenThisYet ) ;
}
return ;
}
if ( Objects [ i ] . _oSelFlag = = 0 ) {
return ;
}
if ( ! deltaload )
PlaySfxLoc ( IS_CHEST , Objects [ i ] . position ) ;
Objects [ i ] . _oSelFlag = 0 ;
Objects [ i ] . _oAnimFrame + = 2 ;
if ( ! deltaload ) {
Point pos = GetSuperItemLoc ( Objects [ i ] . position ) ;
SpawnQuestItem ( IDI_BANNER , pos , 0 , 0 ) ;
}
}
void OperateSlainHero ( int pnum , int i )
{
if ( Objects [ i ] . _oSelFlag = = 0 ) {
return ;
}
Objects [ i ] . _oSelFlag = 0 ;
if ( deltaload ) {
return ;
}
auto & player = Players [ pnum ] ;
if ( player . _pClass = = HeroClass : : Warrior ) {
CreateMagicArmor ( Objects [ i ] . position , ITYPE_HARMOR , ICURS_BREAST_PLATE , false , true ) ;
} else if ( player . _pClass = = HeroClass : : Rogue ) {
CreateMagicWeapon ( Objects [ i ] . position , ITYPE_BOW , ICURS_LONG_WAR_BOW , false , true ) ;
} else if ( player . _pClass = = HeroClass : : Sorcerer ) {
CreateSpellBook ( Objects [ i ] . position , SPL_LIGHTNING , false , true ) ;
} else if ( player . _pClass = = HeroClass : : Monk ) {
CreateMagicWeapon ( Objects [ i ] . position , ITYPE_STAFF , ICURS_WAR_STAFF , false , true ) ;
} else if ( player . _pClass = = HeroClass : : Bard ) {
CreateMagicWeapon ( Objects [ i ] . position , ITYPE_SWORD , ICURS_BASTARD_SWORD , false , true ) ;
} else if ( player . _pClass = = HeroClass : : Barbarian ) {
CreateMagicWeapon ( Objects [ i ] . position , ITYPE_AXE , ICURS_BATTLE_AXE , false , true ) ;
}
Players [ MyPlayerId ] . Say ( HeroSpeech : : RestInPeaceMyFriend ) ;
if ( pnum = = MyPlayerId )
NetSendCmdParam1 ( false , CMD_OPERATEOBJ , i ) ;
}
void OperateTrapLvr ( int i )
{
if ( ! deltaload )
PlaySfxLoc ( IS_LEVER , Objects [ i ] . position ) ;
if ( Objects [ i ] . _oAnimFrame = = 1 ) {
Objects [ i ] . _oAnimFrame = 2 ;
for ( int j = 0 ; j < ActiveObjectCount ; j + + ) {
int oi = ActiveObjects [ j ] ;
if ( Objects [ oi ] . _otype = = Objects [ i ] . _oVar2 & & Objects [ oi ] . _oVar1 = = Objects [ i ] . _oVar1 ) {
Objects [ oi ] . _oVar2 = 1 ;
Objects [ oi ] . _oAnimFlag = 0 ;
}
}
return ;
}
Objects [ i ] . _oAnimFrame - - ;
for ( int j = 0 ; j < ActiveObjectCount ; j + + ) {
int oi = ActiveObjects [ j ] ;
if ( Objects [ oi ] . _otype = = Objects [ i ] . _oVar2 & & Objects [ oi ] . _oVar1 = = Objects [ i ] . _oVar1 ) {
Objects [ oi ] . _oVar2 = 0 ;
if ( Objects [ oi ] . _oVar4 ! = 0 )
Objects [ oi ] . _oAnimFlag = 1 ;
}
}
}
void OperateSarc ( int pnum , int i , bool sendmsg )
{
if ( Objects [ i ] . _oSelFlag = = 0 ) {
return ;
}
if ( ! deltaload )
PlaySfxLoc ( IS_SARC , Objects [ i ] . position ) ;
Objects [ i ] . _oSelFlag = 0 ;
if ( deltaload ) {
Objects [ i ] . _oAnimFrame = Objects [ i ] . _oAnimLen ;
return ;
}
Objects [ i ] . _oAnimFlag = 1 ;
Objects [ i ] . _oAnimDelay = 3 ;
SetRndSeed ( Objects [ i ] . _oRndSeed ) ;
if ( Objects [ i ] . _oVar1 < = 2 )
CreateRndItem ( Objects [ i ] . position , false , sendmsg , false ) ;
if ( Objects [ i ] . _oVar1 > = 8 )
SpawnSkeleton ( Objects [ i ] . _oVar2 , Objects [ i ] . position ) ;
if ( pnum = = MyPlayerId )
NetSendCmdParam1 ( false , CMD_OPERATEOBJ , i ) ;
}
void OperateL2Door ( int pnum , int i , bool sendflag )
{
int dpx = abs ( Objects [ i ] . position . x - Players [ pnum ] . position . tile . x ) ;
int dpy = abs ( Objects [ i ] . position . y - Players [ pnum ] . position . tile . y ) ;
if ( dpx = = 1 & & dpy < = 1 & & Objects [ i ] . _otype = = OBJ_L2LDOOR )
OperateL2LDoor ( pnum , i , sendflag ) ;
if ( dpx < = 1 & & dpy = = 1 & & Objects [ i ] . _otype = = OBJ_L2RDOOR )
OperateL2RDoor ( pnum , i , sendflag ) ;
}
void OperateL3Door ( int pnum , int i , bool sendflag )
{
int dpx = abs ( Objects [ i ] . position . x - Players [ pnum ] . position . tile . x ) ;
int dpy = abs ( Objects [ i ] . position . y - Players [ pnum ] . position . tile . y ) ;
if ( dpx = = 1 & & dpy < = 1 & & Objects [ i ] . _otype = = OBJ_L3RDOOR )
OperateL3RDoor ( pnum , i , sendflag ) ;
if ( dpx < = 1 & & dpy = = 1 & & Objects [ i ] . _otype = = OBJ_L3LDOOR )
OperateL3LDoor ( pnum , i , sendflag ) ;
}
void OperatePedistal ( int pnum , int i )
{
if ( ActiveItemCount > = MAXITEMS ) {
return ;
}
if ( Objects [ i ] . _oVar6 = = 3 | | ! Players [ pnum ] . TryRemoveInvItemById ( IDI_BLDSTONE ) ) {
return ;
}
Objects [ i ] . _oAnimFrame + + ;
Objects [ i ] . _oVar6 + + ;
if ( Objects [ i ] . _oVar6 = = 1 ) {
if ( ! deltaload )
PlaySfxLoc ( LS_PUDDLE , Objects [ i ] . position ) ;
ObjChangeMap ( setpc_x , setpc_y + 3 , setpc_x + 2 , setpc_y + 7 ) ;
SpawnQuestItem ( IDI_BLDSTONE , { 2 * setpc_x + 19 , 2 * setpc_y + 26 } , 0 , 1 ) ;
}
if ( Objects [ i ] . _oVar6 = = 2 ) {
if ( ! deltaload )
PlaySfxLoc ( LS_PUDDLE , Objects [ i ] . position ) ;
ObjChangeMap ( setpc_x + 6 , setpc_y + 3 , setpc_x + setpc_w , setpc_y + 7 ) ;
SpawnQuestItem ( IDI_BLDSTONE , { 2 * setpc_x + 31 , 2 * setpc_y + 26 } , 0 , 1 ) ;
}
if ( Objects [ i ] . _oVar6 = = 3 ) {
if ( ! deltaload )
PlaySfxLoc ( LS_BLODSTAR , Objects [ i ] . position ) ;
ObjChangeMap ( Objects [ i ] . _oVar1 , Objects [ i ] . _oVar2 , Objects [ i ] . _oVar3 , Objects [ i ] . _oVar4 ) ;
LoadMapObjs ( " Levels \\ L2Data \\ Blood2.DUN " , { 2 * setpc_x , 2 * setpc_y } ) ;
SpawnUnique ( UITEM_ARMOFVAL , Point { setpc_x , setpc_y } * 2 + Displacement { 25 , 19 } ) ;
Objects [ i ] . _oSelFlag = 0 ;
}
}
bool OperateShrineMysterious ( int pnum )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return false ;
ModifyPlrStr ( pnum , - 1 ) ;
ModifyPlrMag ( pnum , - 1 ) ;
ModifyPlrDex ( pnum , - 1 ) ;
ModifyPlrVit ( pnum , - 1 ) ;
switch ( static_cast < CharacterAttribute > ( GenerateRnd ( 4 ) ) ) {
case CharacterAttribute : : Strength :
ModifyPlrStr ( pnum , 6 ) ;
break ;
case CharacterAttribute : : Magic :
ModifyPlrMag ( pnum , 6 ) ;
break ;
case CharacterAttribute : : Dexterity :
ModifyPlrDex ( pnum , 6 ) ;
break ;
case CharacterAttribute : : Vitality :
ModifyPlrVit ( pnum , 6 ) ;
break ;
}
CheckStats ( Players [ pnum ] ) ;
InitDiabloMsg ( EMSG_SHRINE_MYSTERIOUS ) ;
return true ;
}
bool OperateShrineHidden ( int pnum )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return false ;
auto & player = Players [ pnum ] ;
int cnt = 0 ;
for ( const auto & item : player . InvBody ) {
if ( ! item . isEmpty ( ) )
cnt + + ;
}
if ( cnt > 0 ) {
for ( auto & item : player . InvBody ) {
if ( ! item . isEmpty ( )
& & item . _iMaxDur ! = DUR_INDESTRUCTIBLE
& & item . _iMaxDur ! = 0 ) {
item . _iDurability + = 10 ;
item . _iMaxDur + = 10 ;
if ( item . _iDurability > item . _iMaxDur )
item . _iDurability = item . _iMaxDur ;
}
}
while ( true ) {
cnt = 0 ;
for ( auto & item : player . InvBody ) {
if ( ! item . isEmpty ( ) & & item . _iMaxDur ! = DUR_INDESTRUCTIBLE & & item . _iMaxDur ! = 0 ) {
cnt + + ;
}
}
if ( cnt = = 0 )
break ;
int r = GenerateRnd ( NUM_INVLOC ) ;
if ( player . InvBody [ r ] . isEmpty ( ) | | player . InvBody [ r ] . _iMaxDur = = DUR_INDESTRUCTIBLE | | player . InvBody [ r ] . _iMaxDur = = 0 )
continue ;
player . InvBody [ r ] . _iDurability - = 20 ;
player . InvBody [ r ] . _iMaxDur - = 20 ;
if ( player . InvBody [ r ] . _iDurability < = 0 )
player . InvBody [ r ] . _iDurability = 1 ;
if ( player . InvBody [ r ] . _iMaxDur < = 0 )
player . InvBody [ r ] . _iMaxDur = 1 ;
break ;
}
}
InitDiabloMsg ( EMSG_SHRINE_HIDDEN ) ;
return true ;
}
bool OperateShrineGloomy ( int pnum )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return true ;
auto & player = Players [ pnum ] ;
if ( ! player . InvBody [ INVLOC_HEAD ] . isEmpty ( ) )
player . InvBody [ INVLOC_HEAD ] . _iAC + = 2 ;
if ( ! player . InvBody [ INVLOC_CHEST ] . isEmpty ( ) )
player . InvBody [ INVLOC_CHEST ] . _iAC + = 2 ;
if ( ! player . InvBody [ INVLOC_HAND_LEFT ] . isEmpty ( ) ) {
if ( player . InvBody [ INVLOC_HAND_LEFT ] . _itype = = ITYPE_SHIELD ) {
player . InvBody [ INVLOC_HAND_LEFT ] . _iAC + = 2 ;
} else {
player . InvBody [ INVLOC_HAND_LEFT ] . _iMaxDam - - ;
if ( player . InvBody [ INVLOC_HAND_LEFT ] . _iMaxDam < player . InvBody [ INVLOC_HAND_LEFT ] . _iMinDam )
player . InvBody [ INVLOC_HAND_LEFT ] . _iMaxDam = player . InvBody [ INVLOC_HAND_LEFT ] . _iMinDam ;
}
}
if ( ! player . InvBody [ INVLOC_HAND_RIGHT ] . isEmpty ( ) ) {
if ( player . InvBody [ INVLOC_HAND_RIGHT ] . _itype = = ITYPE_SHIELD ) {
player . InvBody [ INVLOC_HAND_RIGHT ] . _iAC + = 2 ;
} else {
player . InvBody [ INVLOC_HAND_RIGHT ] . _iMaxDam - - ;
if ( player . InvBody [ INVLOC_HAND_RIGHT ] . _iMaxDam < player . InvBody [ INVLOC_HAND_RIGHT ] . _iMinDam )
player . InvBody [ INVLOC_HAND_RIGHT ] . _iMaxDam = player . InvBody [ INVLOC_HAND_RIGHT ] . _iMinDam ;
}
}
for ( int j = 0 ; j < player . _pNumInv ; j + + ) {
switch ( player . InvList [ j ] . _itype ) {
case ITYPE_SWORD :
case ITYPE_AXE :
case ITYPE_BOW :
case ITYPE_MACE :
case ITYPE_STAFF :
player . InvList [ j ] . _iMaxDam - - ;
if ( player . InvList [ j ] . _iMaxDam < player . InvList [ j ] . _iMinDam )
player . InvList [ j ] . _iMaxDam = player . InvList [ j ] . _iMinDam ;
break ;
case ITYPE_SHIELD :
case ITYPE_HELM :
case ITYPE_LARMOR :
case ITYPE_MARMOR :
case ITYPE_HARMOR :
player . InvList [ j ] . _iAC + = 2 ;
break ;
default :
break ;
}
}
InitDiabloMsg ( EMSG_SHRINE_GLOOMY ) ;
return true ;
}
bool OperateShrineWeird ( int pnum )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return true ;
auto & player = Players [ pnum ] ;
if ( ! player . InvBody [ INVLOC_HAND_LEFT ] . isEmpty ( ) & & player . InvBody [ INVLOC_HAND_LEFT ] . _itype ! = ITYPE_SHIELD )
player . InvBody [ INVLOC_HAND_LEFT ] . _iMaxDam + + ;
if ( ! player . InvBody [ INVLOC_HAND_RIGHT ] . isEmpty ( ) & & player . InvBody [ INVLOC_HAND_RIGHT ] . _itype ! = ITYPE_SHIELD )
player . InvBody [ INVLOC_HAND_RIGHT ] . _iMaxDam + + ;
for ( int j = 0 ; j < player . _pNumInv ; j + + ) {
switch ( player . InvList [ j ] . _itype ) {
case ITYPE_SWORD :
case ITYPE_AXE :
case ITYPE_BOW :
case ITYPE_MACE :
case ITYPE_STAFF :
player . InvList [ j ] . _iMaxDam + + ;
break ;
default :
break ;
}
}
InitDiabloMsg ( EMSG_SHRINE_WEIRD ) ;
return true ;
}
bool OperateShrineMagical ( int pnum )
{
if ( deltaload )
return false ;
auto & player = Players [ pnum ] ;
AddMissile (
player . position . tile ,
player . position . tile ,
player . _pdir ,
MIS_MANASHIELD ,
TARGET_PLAYERS ,
pnum ,
0 ,
2 * leveltype ) ;
if ( pnum ! = MyPlayerId )
return false ;
InitDiabloMsg ( EMSG_SHRINE_MAGICAL ) ;
return true ;
}
bool OperateShrineStone ( int pnum )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return true ;
auto & player = Players [ pnum ] ;
for ( auto & item : player . InvBody ) {
if ( item . _itype = = ITYPE_STAFF )
item . _iCharges = item . _iMaxCharges ;
}
for ( int j = 0 ; j < player . _pNumInv ; j + + ) {
if ( player . InvList [ j ] . _itype = = ITYPE_STAFF )
player . InvList [ j ] . _iCharges = player . InvList [ j ] . _iMaxCharges ;
}
for ( auto & item : player . SpdList ) {
if ( item . _itype = = ITYPE_STAFF )
item . _iCharges = item . _iMaxCharges ; // belt items don't have charges?
}
InitDiabloMsg ( EMSG_SHRINE_STONE ) ;
return true ;
}
bool OperateShrineReligious ( int pnum )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return true ;
auto & player = Players [ pnum ] ;
for ( auto & item : player . InvBody )
item . _iDurability = item . _iMaxDur ;
for ( int j = 0 ; j < player . _pNumInv ; j + + )
player . InvList [ j ] . _iDurability = player . InvList [ j ] . _iMaxDur ;
for ( auto & item : player . SpdList )
item . _iDurability = item . _iMaxDur ; // belt items don't have durability?
InitDiabloMsg ( EMSG_SHRINE_RELIGIOUS ) ;
return true ;
}
bool OperateShrineEnchanted ( int pnum )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return false ;
auto & player = Players [ pnum ] ;
int cnt = 0 ;
uint64_t spell = 1 ;
int maxSpells = gbIsHellfire ? MAX_SPELLS : 37 ;
uint64_t spells = player . _pMemSpells ;
for ( int j = 0 ; j < maxSpells ; j + + ) {
if ( ( spell & spells ) ! = 0 )
cnt + + ;
spell * = 2 ;
}
if ( cnt > 1 ) {
spell = 1 ;
for ( int j = SPL_FIREBOLT ; j < maxSpells ; j + + ) { // BUGFIX: < MAX_SPELLS, there is no spell with MAX_SPELLS index (fixed)
if ( ( player . _pMemSpells & spell ) ! = 0 ) {
if ( player . _pSplLvl [ j ] < MAX_SPELL_LEVEL )
player . _pSplLvl [ j ] + + ;
}
spell * = 2 ;
}
int r ;
do {
r = GenerateRnd ( maxSpells ) ;
} while ( ( player . _pMemSpells & GetSpellBitmask ( r + 1 ) ) = = 0 ) ;
if ( player . _pSplLvl [ r + 1 ] > = 2 )
player . _pSplLvl [ r + 1 ] - = 2 ;
else
player . _pSplLvl [ r + 1 ] = 0 ;
}
InitDiabloMsg ( EMSG_SHRINE_ENCHANTED ) ;
return true ;
}
bool OperateShrineThaumaturgic ( int pnum )
{
for ( int j = 0 ; j < ActiveObjectCount ; j + + ) {
int v1 = ActiveObjects [ j ] ;
assert ( v1 > = 0 & & v1 < MAXOBJECTS ) ;
if ( IsAnyOf ( Objects [ v1 ] . _otype , OBJ_CHEST1 , OBJ_CHEST2 , OBJ_CHEST3 , OBJ_TCHEST1 , OBJ_TCHEST2 , OBJ_TCHEST3 ) & & Objects [ v1 ] . _oSelFlag = = 0 ) {
Objects [ v1 ] . _oRndSeed = AdvanceRndSeed ( ) ;
Objects [ v1 ] . _oSelFlag = 1 ;
Objects [ v1 ] . _oAnimFrame - = 2 ;
}
}
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return true ;
InitDiabloMsg ( EMSG_SHRINE_THAUMATURGIC ) ;
return true ;
}
bool OperateShrineFascinating ( int pnum )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return false ;
auto & player = Players [ pnum ] ;
player . _pMemSpells | = GetSpellBitmask ( SPL_FIREBOLT ) ;
if ( player . _pSplLvl [ SPL_FIREBOLT ] < MAX_SPELL_LEVEL )
player . _pSplLvl [ SPL_FIREBOLT ] + + ;
if ( player . _pSplLvl [ SPL_FIREBOLT ] < MAX_SPELL_LEVEL )
player . _pSplLvl [ SPL_FIREBOLT ] + + ;
DWORD t = player . _pMaxManaBase / 10 ;
int v1 = player . _pMana - player . _pManaBase ;
int v2 = player . _pMaxMana - player . _pMaxManaBase ;
player . _pManaBase - = t ;
player . _pMana - = t ;
player . _pMaxMana - = t ;
player . _pMaxManaBase - = t ;
if ( player . _pMana > > 6 < = 0 ) {
player . _pMana = v1 ;
player . _pManaBase = 0 ;
}
if ( player . _pMaxMana > > 6 < = 0 ) {
player . _pMaxMana = v2 ;
player . _pMaxManaBase = 0 ;
}
InitDiabloMsg ( EMSG_SHRINE_FASCINATING ) ;
return true ;
}
bool OperateShrineCryptic ( int pnum )
{
if ( deltaload )
return false ;
auto & player = Players [ pnum ] ;
AddMissile (
player . position . tile ,
player . position . tile ,
player . _pdir ,
MIS_NOVA ,
TARGET_PLAYERS ,
pnum ,
0 ,
2 * leveltype ) ;
if ( pnum ! = MyPlayerId )
return false ;
player . _pMana = player . _pMaxMana ;
player . _pManaBase = player . _pMaxManaBase ;
InitDiabloMsg ( EMSG_SHRINE_CRYPTIC ) ;
return true ;
}
bool OperateShrineEldritch ( int pnum )
{
/// BUGFIX: change `plr[pnum].HoldItem` to use a temporary buffer to prevent deleting item in hand
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return true ;
auto & player = Players [ pnum ] ;
for ( int j = 0 ; j < player . _pNumInv ; j + + ) {
if ( player . InvList [ j ] . _itype = = ITYPE_MISC ) {
if ( player . InvList [ j ] . _iMiscId = = IMISC_HEAL
| | player . InvList [ j ] . _iMiscId = = IMISC_MANA ) {
SetPlrHandItem ( player . HoldItem , ItemMiscIdIdx ( IMISC_REJUV ) ) ;
GetPlrHandSeed ( & player . HoldItem ) ;
player . HoldItem . _iStatFlag = true ;
player . InvList [ j ] = player . HoldItem ;
}
if ( player . InvList [ j ] . _iMiscId = = IMISC_FULLHEAL
| | player . InvList [ j ] . _iMiscId = = IMISC_FULLMANA ) {
SetPlrHandItem ( player . HoldItem , ItemMiscIdIdx ( IMISC_FULLREJUV ) ) ;
GetPlrHandSeed ( & player . HoldItem ) ;
player . HoldItem . _iStatFlag = true ;
player . InvList [ j ] = player . HoldItem ;
}
}
}
for ( auto & item : player . SpdList ) {
if ( item . _itype = = ITYPE_MISC ) {
if ( item . _iMiscId = = IMISC_HEAL
| | item . _iMiscId = = IMISC_MANA ) {
SetPlrHandItem ( player . HoldItem , ItemMiscIdIdx ( IMISC_REJUV ) ) ;
GetPlrHandSeed ( & player . HoldItem ) ;
player . HoldItem . _iStatFlag = true ;
item = player . HoldItem ;
}
if ( item . _iMiscId = = IMISC_FULLHEAL
| | item . _iMiscId = = IMISC_FULLMANA ) {
SetPlrHandItem ( player . HoldItem , ItemMiscIdIdx ( IMISC_FULLREJUV ) ) ;
GetPlrHandSeed ( & player . HoldItem ) ;
player . HoldItem . _iStatFlag = true ;
item = player . HoldItem ;
}
}
}
InitDiabloMsg ( EMSG_SHRINE_ELDRITCH ) ;
return true ;
}
bool OperateShrineEerie ( int pnum )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return false ;
ModifyPlrMag ( pnum , 2 ) ;
CheckStats ( Players [ pnum ] ) ;
InitDiabloMsg ( EMSG_SHRINE_EERIE ) ;
return true ;
}
/**
* @ brief Fully restores HP and Mana of the active player and spawns a pair of potions
* in response to the player activating a Divine shrine
* @ param pnum The player that activated the shrine
* @ param spawnPosition The map tile where the potions will be spawned
* @ return false if the shrine was activated by another player in a multiplayer game and
* no changes were made by this instance , true otherwise .
*/
bool OperateShrineDivine ( int pnum , Point spawnPosition )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return false ;
auto & player = Players [ pnum ] ;
if ( currlevel < 4 ) {
CreateTypeItem ( spawnPosition , false , ITYPE_MISC , IMISC_FULLMANA , false , true ) ;
CreateTypeItem ( spawnPosition , false , ITYPE_MISC , IMISC_FULLHEAL , false , true ) ;
} else {
CreateTypeItem ( spawnPosition , false , ITYPE_MISC , IMISC_FULLREJUV , false , true ) ;
CreateTypeItem ( spawnPosition , false , ITYPE_MISC , IMISC_FULLREJUV , false , true ) ;
}
player . _pMana = player . _pMaxMana ;
player . _pManaBase = player . _pMaxManaBase ;
player . _pHitPoints = player . _pMaxHP ;
player . _pHPBase = player . _pMaxHPBase ;
InitDiabloMsg ( EMSG_SHRINE_DIVINE ) ;
return true ;
}
bool OperateShrineHoly ( int pnum )
{
if ( deltaload )
return false ;
auto & player = Players [ pnum ] ;
int j = 0 ;
int xx ;
int yy ;
uint32_t lv ;
do {
xx = GenerateRnd ( MAXDUNX ) ;
yy = GenerateRnd ( MAXDUNY ) ;
lv = dPiece [ xx ] [ yy ] ;
j + + ;
if ( j > MAXDUNX * MAXDUNY )
break ;
} while ( nSolidTable [ lv ] | | dObject [ xx ] [ yy ] ! = 0 | | dMonster [ xx ] [ yy ] ! = 0 ) ;
AddMissile ( player . position . tile , { xx , yy } , player . _pdir , MIS_RNDTELEPORT , TARGET_PLAYERS , pnum , 0 , 2 * leveltype ) ;
if ( pnum ! = MyPlayerId )
return false ;
InitDiabloMsg ( EMSG_SHRINE_HOLY ) ;
return true ;
}
bool OperateShrineSacred ( int pnum )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return false ;
auto & player = Players [ pnum ] ;
player . _pMemSpells | = GetSpellBitmask ( SPL_CBOLT ) ;
if ( player . _pSplLvl [ SPL_CBOLT ] < MAX_SPELL_LEVEL )
player . _pSplLvl [ SPL_CBOLT ] + + ;
if ( player . _pSplLvl [ SPL_CBOLT ] < MAX_SPELL_LEVEL )
player . _pSplLvl [ SPL_CBOLT ] + + ;
uint32_t t = player . _pMaxManaBase / 10 ;
int v1 = player . _pMana - player . _pManaBase ;
int v2 = player . _pMaxMana - player . _pMaxManaBase ;
player . _pManaBase - = t ;
player . _pMana - = t ;
player . _pMaxMana - = t ;
player . _pMaxManaBase - = t ;
if ( player . _pMana > > 6 < = 0 ) {
player . _pMana = v1 ;
player . _pManaBase = 0 ;
}
if ( player . _pMaxMana > > 6 < = 0 ) {
player . _pMaxMana = v2 ;
player . _pMaxManaBase = 0 ;
}
InitDiabloMsg ( EMSG_SHRINE_SACRED ) ;
return true ;
}
bool OperateShrineSpiritual ( int pnum )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return false ;
auto & player = Players [ pnum ] ;
for ( int8_t & gridItem : player . InvGrid ) {
if ( gridItem = = 0 ) {
int r = 5 * leveltype + GenerateRnd ( 10 * leveltype ) ;
DWORD t = player . _pNumInv ; // check
player . InvList [ t ] = golditem ;
player . InvList [ t ] . _iSeed = AdvanceRndSeed ( ) ;
player . _pNumInv + + ;
gridItem = player . _pNumInv ;
player . InvList [ t ] . _ivalue = r ;
player . _pGold + = r ;
SetPlrHandGoldCurs ( player . InvList [ t ] ) ;
}
}
InitDiabloMsg ( EMSG_SHRINE_SPIRITUAL ) ;
return true ;
}
bool OperateShrineSpooky ( int pnum )
{
if ( deltaload )
return false ;
if ( pnum = = MyPlayerId ) {
InitDiabloMsg ( EMSG_SHRINE_SPOOKY1 ) ;
return true ;
}
auto & myPlayer = Players [ MyPlayerId ] ;
myPlayer . _pHitPoints = myPlayer . _pMaxHP ;
myPlayer . _pHPBase = myPlayer . _pMaxHPBase ;
myPlayer . _pMana = myPlayer . _pMaxMana ;
myPlayer . _pManaBase = myPlayer . _pMaxManaBase ;
InitDiabloMsg ( EMSG_SHRINE_SPOOKY2 ) ;
return true ;
}
bool OperateShrineAbandoned ( int pnum )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return false ;
ModifyPlrDex ( pnum , 2 ) ;
CheckStats ( Players [ pnum ] ) ;
if ( pnum ! = MyPlayerId )
return true ;
InitDiabloMsg ( EMSG_SHRINE_ABANDONED ) ;
return true ;
}
bool OperateShrineCreepy ( int pnum )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return false ;
ModifyPlrStr ( pnum , 2 ) ;
CheckStats ( Players [ pnum ] ) ;
if ( pnum ! = MyPlayerId )
return true ;
InitDiabloMsg ( EMSG_SHRINE_CREEPY ) ;
return true ;
}
bool OperateShrineQuiet ( int pnum )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return false ;
ModifyPlrVit ( pnum , 2 ) ;
CheckStats ( Players [ pnum ] ) ;
if ( pnum ! = MyPlayerId )
return true ;
InitDiabloMsg ( EMSG_SHRINE_QUIET ) ;
return true ;
}
bool OperateShrineSecluded ( int pnum )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return true ;
std : : fill ( & AutomapView [ 0 ] [ 0 ] , & AutomapView [ DMAXX - 1 ] [ DMAXX - 1 ] , true ) ;
InitDiabloMsg ( EMSG_SHRINE_SECLUDED ) ;
return true ;
}
bool OperateShrineOrnate ( int pnum )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return false ;
auto & player = Players [ pnum ] ;
player . _pMemSpells | = GetSpellBitmask ( SPL_HBOLT ) ;
if ( player . _pSplLvl [ SPL_HBOLT ] < MAX_SPELL_LEVEL )
player . _pSplLvl [ SPL_HBOLT ] + + ;
if ( player . _pSplLvl [ SPL_HBOLT ] < MAX_SPELL_LEVEL )
player . _pSplLvl [ SPL_HBOLT ] + + ;
uint32_t t = player . _pMaxManaBase / 10 ;
int v1 = player . _pMana - player . _pManaBase ;
int v2 = player . _pMaxMana - player . _pMaxManaBase ;
player . _pManaBase - = t ;
player . _pMana - = t ;
player . _pMaxMana - = t ;
player . _pMaxManaBase - = t ;
if ( player . _pMana > > 6 < = 0 ) {
player . _pMana = v1 ;
player . _pManaBase = 0 ;
}
if ( player . _pMaxMana > > 6 < = 0 ) {
player . _pMaxMana = v2 ;
player . _pMaxManaBase = 0 ;
}
InitDiabloMsg ( EMSG_SHRINE_ORNATE ) ;
return true ;
}
bool OperateShrineGlimmering ( int pnum )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return false ;
auto & player = Players [ pnum ] ;
for ( auto & item : player . InvBody ) {
if ( item . _iMagical ! = ITEM_QUALITY_NORMAL & & ! item . _iIdentified )
item . _iIdentified = true ;
}
for ( int j = 0 ; j < player . _pNumInv ; j + + ) {
if ( player . InvList [ j ] . _iMagical ! = ITEM_QUALITY_NORMAL & & ! player . InvList [ j ] . _iIdentified )
player . InvList [ j ] . _iIdentified = true ;
}
for ( auto & item : player . SpdList ) {
if ( item . _iMagical ! = ITEM_QUALITY_NORMAL & & ! item . _iIdentified )
item . _iIdentified = true ; // belt items can't be magical?
}
InitDiabloMsg ( EMSG_SHRINE_GLIMMERING ) ;
return true ;
}
bool OperateShrineTainted ( int pnum )
{
if ( deltaload )
return false ;
if ( pnum = = MyPlayerId ) {
InitDiabloMsg ( EMSG_SHRINE_TAINTED1 ) ;
return true ;
}
int r = GenerateRnd ( 4 ) ;
int v1 = r = = 0 ? 1 : - 1 ;
int v2 = r = = 1 ? 1 : - 1 ;
int v3 = r = = 2 ? 1 : - 1 ;
int v4 = r = = 3 ? 1 : - 1 ;
ModifyPlrStr ( MyPlayerId , v1 ) ;
ModifyPlrMag ( MyPlayerId , v2 ) ;
ModifyPlrDex ( MyPlayerId , v3 ) ;
ModifyPlrVit ( MyPlayerId , v4 ) ;
CheckStats ( Players [ MyPlayerId ] ) ;
InitDiabloMsg ( EMSG_SHRINE_TAINTED2 ) ;
return true ;
}
/**
* @ brief Oily shrines increase the players primary stat ( s ) by a total of two , but spawn a
* firewall near the shrine that will spread towards the player
* @ param pnum The player that activated the shrine
* @ param spawnPosition Start location for the firewall
* @ return false if the current player did not activate the shrine ( i . e . it ' s a multiplayer
* game ) and we bailed early to avoid doubling the effects , true otherwise .
*/
bool OperateShrineOily ( int pnum , Point spawnPosition )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return false ;
auto & myPlayer = Players [ MyPlayerId ] ;
switch ( myPlayer . _pClass ) {
case HeroClass : : Warrior :
ModifyPlrStr ( MyPlayerId , 2 ) ;
break ;
case HeroClass : : Rogue :
ModifyPlrDex ( MyPlayerId , 2 ) ;
break ;
case HeroClass : : Sorcerer :
ModifyPlrMag ( MyPlayerId , 2 ) ;
break ;
case HeroClass : : Barbarian :
ModifyPlrVit ( MyPlayerId , 2 ) ;
break ;
case HeroClass : : Monk :
ModifyPlrStr ( MyPlayerId , 1 ) ;
ModifyPlrDex ( MyPlayerId , 1 ) ;
break ;
case HeroClass : : Bard :
ModifyPlrDex ( MyPlayerId , 1 ) ;
ModifyPlrMag ( MyPlayerId , 1 ) ;
break ;
}
CheckStats ( Players [ pnum ] ) ;
AddMissile (
spawnPosition ,
myPlayer . position . tile ,
myPlayer . _pdir ,
MIS_FIREWALL ,
TARGET_PLAYERS ,
- 1 ,
2 * currlevel + 2 ,
0 ) ;
InitDiabloMsg ( EMSG_SHRINE_OILY ) ;
return true ;
}
bool OperateShrineGlowing ( int pnum )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return false ;
auto & myPlayer = Players [ MyPlayerId ] ;
// Add 0-5 points to Magic (0.1% of the players XP)
ModifyPlrMag ( MyPlayerId , static_cast < int > ( std : : min < uint32_t > ( myPlayer . _pExperience / 1000 , 5 ) ) ) ;
// Take 5% of the players experience to offset the bonus, unless they're very low level in which case take all their experience.
if ( myPlayer . _pExperience > 5000 )
myPlayer . _pExperience = static_cast < uint32_t > ( myPlayer . _pExperience * 0.95 ) ;
else
myPlayer . _pExperience = 0 ;
if ( sgOptions . Gameplay . bExperienceBar )
force_redraw = 255 ;
CheckStats ( Players [ pnum ] ) ;
InitDiabloMsg ( EMSG_SHRINE_GLOWING ) ;
return true ;
}
bool OperateShrineMendicant ( int pnum )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return false ;
auto & myPlayer = Players [ MyPlayerId ] ;
int gold = myPlayer . _pGold / 2 ;
AddPlrExperience ( MyPlayerId , myPlayer . _pLevel , gold ) ;
TakePlrsMoney ( gold ) ;
CheckStats ( Players [ pnum ] ) ;
InitDiabloMsg ( EMSG_SHRINE_MENDICANT ) ;
return true ;
}
/**
* @ brief Grants experience to the player based on their current level while also triggering a magic trap
* @ param pnum The player that activated the shrine
* @ param spawnPosition The trap results in casting flash from this location targeting the player
* @ return false if the current player didn ' t activate the shrine ( to avoid doubling the effect ) , true otherwise
*/
bool OperateShrineSparkling ( int pnum , Point spawnPosition )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return false ;
auto & myPlayer = Players [ MyPlayerId ] ;
AddPlrExperience ( MyPlayerId , myPlayer . _pLevel , 1000 * currlevel ) ;
AddMissile (
spawnPosition ,
myPlayer . position . tile ,
myPlayer . _pdir ,
MIS_FLASH ,
TARGET_PLAYERS ,
- 1 ,
3 * currlevel + 2 ,
0 ) ;
CheckStats ( Players [ pnum ] ) ;
InitDiabloMsg ( EMSG_SHRINE_SPARKLING ) ;
return true ;
}
/**
* @ brief Spawns a town portal near the active player
* @ param pnum The player that activated the shrine
* @ param spawnPosition The position of the shrine , the portal will be placed on the side closest to the player
* @ return false if the current player didn ' t activate the shrine ( to avoid doubling the effect ) , true otherwise
*/
bool OperateShrineTown ( int pnum , Point spawnPosition )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return false ;
auto & myPlayer = Players [ MyPlayerId ] ;
AddMissile (
spawnPosition ,
myPlayer . position . tile ,
myPlayer . _pdir ,
MIS_TOWN ,
TARGET_PLAYERS ,
pnum ,
0 ,
0 ) ;
InitDiabloMsg ( EMSG_SHRINE_TOWN ) ;
return true ;
}
bool OperateShrineShimmering ( int pnum )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return false ;
auto & player = Players [ pnum ] ;
player . _pMana = player . _pMaxMana ;
player . _pManaBase = player . _pMaxManaBase ;
InitDiabloMsg ( EMSG_SHRINE_SHIMMERING ) ;
return true ;
}
bool OperateShrineSolar ( int pnum )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return false ;
time_t tm = time ( nullptr ) ;
int hour = localtime ( & tm ) - > tm_hour ;
if ( hour > = 20 | | hour < 4 ) {
InitDiabloMsg ( EMSG_SHRINE_SOLAR4 ) ;
ModifyPlrVit ( MyPlayerId , 2 ) ;
} else if ( hour > = 18 ) {
InitDiabloMsg ( EMSG_SHRINE_SOLAR3 ) ;
ModifyPlrMag ( MyPlayerId , 2 ) ;
} else if ( hour > = 12 ) {
InitDiabloMsg ( EMSG_SHRINE_SOLAR2 ) ;
ModifyPlrStr ( MyPlayerId , 2 ) ;
} else /* 4:00 to 11:59 */ {
InitDiabloMsg ( EMSG_SHRINE_SOLAR1 ) ;
ModifyPlrDex ( MyPlayerId , 2 ) ;
}
CheckStats ( Players [ pnum ] ) ;
return true ;
}
bool OperateShrineMurphys ( int pnum )
{
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return false ;
auto & myPlayer = Players [ MyPlayerId ] ;
bool broke = false ;
for ( auto & item : myPlayer . InvBody ) {
if ( ! item . isEmpty ( ) & & GenerateRnd ( 3 ) = = 0 ) {
if ( item . _iDurability ! = DUR_INDESTRUCTIBLE ) {
if ( item . _iDurability > 0 ) {
item . _iDurability / = 2 ;
broke = true ;
break ;
}
}
}
}
if ( ! broke ) {
TakePlrsMoney ( myPlayer . _pGold / 3 ) ;
}
InitDiabloMsg ( EMSG_SHRINE_MURPHYS ) ;
return true ;
}
void OperateShrine ( int pnum , int i , _sfx_id sType )
{
if ( dropGoldFlag ) {
dropGoldFlag = false ;
dropGoldValue = 0 ;
}
assert ( i > = 0 & & i < MAXOBJECTS ) ;
if ( Objects [ i ] . _oSelFlag = = 0 )
return ;
SetRndSeed ( Objects [ i ] . _oRndSeed ) ;
Objects [ i ] . _oSelFlag = 0 ;
if ( ! deltaload ) {
PlaySfxLoc ( sType , Objects [ i ] . position ) ;
Objects [ i ] . _oAnimFlag = 1 ;
Objects [ i ] . _oAnimDelay = 1 ;
} else {
Objects [ i ] . _oAnimFrame = Objects [ i ] . _oAnimLen ;
Objects [ i ] . _oAnimFlag = 0 ;
}
switch ( Objects [ i ] . _oVar1 ) {
case ShrineMysterious :
if ( ! OperateShrineMysterious ( pnum ) )
return ;
break ;
case ShrineHidden :
if ( ! OperateShrineHidden ( pnum ) )
return ;
break ;
case ShrineGloomy :
if ( ! OperateShrineGloomy ( pnum ) )
return ;
break ;
case ShrineWeird :
if ( ! OperateShrineWeird ( pnum ) )
return ;
break ;
case ShrineMagical :
case ShrineMagicaL2 :
if ( ! OperateShrineMagical ( pnum ) )
return ;
break ;
case ShrineStone :
if ( ! OperateShrineStone ( pnum ) )
return ;
break ;
case ShrineReligious :
if ( ! OperateShrineReligious ( pnum ) )
return ;
break ;
case ShrineEnchanted :
if ( ! OperateShrineEnchanted ( pnum ) )
return ;
break ;
case ShrineThaumaturgic :
if ( ! OperateShrineThaumaturgic ( pnum ) )
return ;
break ;
case ShrineFascinating :
if ( ! OperateShrineFascinating ( pnum ) )
return ;
break ;
case ShrineCryptic :
if ( ! OperateShrineCryptic ( pnum ) )
return ;
break ;
case ShrineEldritch :
if ( ! OperateShrineEldritch ( pnum ) )
return ;
break ;
case ShrineEerie :
if ( ! OperateShrineEerie ( pnum ) )
return ;
break ;
case ShrineDivine :
if ( ! OperateShrineDivine ( pnum , Objects [ i ] . position ) )
return ;
break ;
case ShrineHoly :
if ( ! OperateShrineHoly ( pnum ) )
return ;
break ;
case ShrineSacred :
if ( ! OperateShrineSacred ( pnum ) )
return ;
break ;
case ShrineSpiritual :
if ( ! OperateShrineSpiritual ( pnum ) )
return ;
break ;
case ShrineSpooky :
if ( ! OperateShrineSpooky ( pnum ) )
return ;
break ;
case ShrineAbandoned :
if ( ! OperateShrineAbandoned ( pnum ) )
return ;
break ;
case ShrineCreepy :
if ( ! OperateShrineCreepy ( pnum ) )
return ;
break ;
case ShrineQuiet :
if ( ! OperateShrineQuiet ( pnum ) )
return ;
break ;
case ShrineSecluded :
if ( ! OperateShrineSecluded ( pnum ) )
return ;
break ;
case ShrineOrnate :
if ( ! OperateShrineOrnate ( pnum ) )
return ;
break ;
case ShrineGlimmering :
if ( ! OperateShrineGlimmering ( pnum ) )
return ;
break ;
case ShrineTainted :
if ( ! OperateShrineTainted ( pnum ) )
return ;
break ;
case ShrineOily :
if ( ! OperateShrineOily ( pnum , Objects [ i ] . position ) )
return ;
break ;
case ShrineGlowing :
if ( ! OperateShrineGlowing ( pnum ) )
return ;
break ;
case ShrineMendicant :
if ( ! OperateShrineMendicant ( pnum ) )
return ;
break ;
case ShrineSparkling :
if ( ! OperateShrineSparkling ( pnum , Objects [ i ] . position ) )
return ;
break ;
case ShrineTown :
if ( ! OperateShrineTown ( pnum , Objects [ i ] . position ) )
return ;
break ;
case ShrineShimmering :
if ( ! OperateShrineShimmering ( pnum ) )
return ;
break ;
case ShrineSolar :
if ( ! OperateShrineSolar ( pnum ) )
return ;
break ;
case ShrineMurphys :
if ( ! OperateShrineMurphys ( pnum ) )
return ;
break ;
}
CalcPlrInv ( pnum , true ) ;
force_redraw = 255 ;
if ( pnum = = MyPlayerId )
NetSendCmdParam2 ( false , CMD_PLROPOBJ , pnum , i ) ;
}
void OperateSkelBook ( int pnum , int i , bool sendmsg )
{
if ( Objects [ i ] . _oSelFlag = = 0 ) {
return ;
}
if ( ! deltaload )
PlaySfxLoc ( IS_ISCROL , Objects [ i ] . position ) ;
Objects [ i ] . _oSelFlag = 0 ;
Objects [ i ] . _oAnimFrame + = 2 ;
if ( deltaload ) {
return ;
}
SetRndSeed ( Objects [ i ] . _oRndSeed ) ;
if ( GenerateRnd ( 5 ) ! = 0 )
CreateTypeItem ( Objects [ i ] . position , false , ITYPE_MISC , IMISC_SCROLL , sendmsg , false ) ;
else
CreateTypeItem ( Objects [ i ] . position , false , ITYPE_MISC , IMISC_BOOK , sendmsg , false ) ;
if ( pnum = = MyPlayerId )
NetSendCmdParam1 ( false , CMD_OPERATEOBJ , i ) ;
}
void OperateBookCase ( int pnum , int i , bool sendmsg )
{
if ( Objects [ i ] . _oSelFlag = = 0 ) {
return ;
}
if ( ! deltaload )
PlaySfxLoc ( IS_ISCROL , Objects [ i ] . position ) ;
Objects [ i ] . _oSelFlag = 0 ;
Objects [ i ] . _oAnimFrame - = 2 ;
if ( deltaload ) {
return ;
}
SetRndSeed ( Objects [ i ] . _oRndSeed ) ;
CreateTypeItem ( Objects [ i ] . position , false , ITYPE_MISC , IMISC_BOOK , sendmsg , false ) ;
if ( Quests [ Q_ZHAR ] . IsAvailable ( ) ) {
auto & zhar = Monsters [ MAX_PLRS ] ;
if ( zhar . _mmode = = MM_STAND // prevents playing the "angry" message for the second time if zhar got aggroed by losing vision and talking again
& & zhar . _uniqtype - 1 = = UMT_ZHAR
& & zhar . _msquelch = = UINT8_MAX
& & zhar . _mhitpoints > 0 ) {
zhar . mtalkmsg = TEXT_ZHAR2 ;
M_StartStand ( zhar , zhar . _mdir ) ; // BUGFIX: first parameter in call to M_StartStand should be MAX_PLRS, not 0. (fixed)
zhar . _mgoal = MGOAL_ATTACK2 ;
zhar . _mmode = MM_TALK ;
}
}
if ( pnum = = MyPlayerId )
NetSendCmdParam1 ( false , CMD_OPERATEOBJ , i ) ;
}
void OperateDecap ( int pnum , int i , bool sendmsg )
{
if ( Objects [ i ] . _oSelFlag = = 0 ) {
return ;
}
Objects [ i ] . _oSelFlag = 0 ;
if ( deltaload ) {
return ;
}
SetRndSeed ( Objects [ i ] . _oRndSeed ) ;
CreateRndItem ( Objects [ i ] . position , false , sendmsg , false ) ;
if ( pnum = = MyPlayerId )
NetSendCmdParam1 ( false , CMD_OPERATEOBJ , i ) ;
}
void OperateArmorStand ( int pnum , int i , bool sendmsg )
{
if ( Objects [ i ] . _oSelFlag = = 0 ) {
return ;
}
Objects [ i ] . _oSelFlag = 0 ;
Objects [ i ] . _oAnimFrame + + ;
if ( deltaload ) {
return ;
}
SetRndSeed ( Objects [ i ] . _oRndSeed ) ;
bool uniqueRnd = ( GenerateRnd ( 2 ) ! = 0 ) ;
if ( currlevel < = 5 ) {
CreateTypeItem ( Objects [ i ] . position , true , ITYPE_LARMOR , IMISC_NONE , sendmsg , false ) ;
} else if ( currlevel > = 6 & & currlevel < = 9 ) {
CreateTypeItem ( Objects [ i ] . position , uniqueRnd , ITYPE_MARMOR , IMISC_NONE , sendmsg , false ) ;
} else if ( currlevel > = 10 & & currlevel < = 12 ) {
CreateTypeItem ( Objects [ i ] . position , false , ITYPE_HARMOR , IMISC_NONE , sendmsg , false ) ;
} else if ( currlevel > = 13 & & currlevel < = 16 ) {
CreateTypeItem ( Objects [ i ] . position , true , ITYPE_HARMOR , IMISC_NONE , sendmsg , false ) ;
} else if ( currlevel > = 17 ) {
CreateTypeItem ( Objects [ i ] . position , true , ITYPE_HARMOR , IMISC_NONE , sendmsg , false ) ;
}
if ( pnum = = MyPlayerId )
NetSendCmdParam1 ( false , CMD_OPERATEOBJ , i ) ;
}
int FindValidShrine ( )
{
bool done = false ;
int rv ;
do {
rv = GenerateRnd ( gbIsHellfire ? NumberOfShrineTypes : 26 ) ;
if ( currlevel > = shrinemin [ rv ] & & currlevel < = shrinemax [ rv ] & & rv ! = ShrineThaumaturgic ) {
done = true ;
}
if ( done ) {
if ( gbIsMultiplayer ) {
if ( shrineavail [ rv ] = = ShrineTypeSingle ) {
done = false ;
continue ;
}
}
if ( ! gbIsMultiplayer ) {
if ( shrineavail [ rv ] = = ShrineTypeMulti ) {
done = false ;
continue ;
}
}
done = true ;
}
} while ( ! done ) ;
return rv ;
}
void OperateGoatShrine ( int pnum , int i , _sfx_id sType )
{
SetRndSeed ( Objects [ i ] . _oRndSeed ) ;
Objects [ i ] . _oVar1 = FindValidShrine ( ) ;
OperateShrine ( pnum , i , sType ) ;
Objects [ i ] . _oAnimDelay = 2 ;
force_redraw = 255 ;
}
void OperateCauldron ( int pnum , int i , _sfx_id sType )
{
SetRndSeed ( Objects [ i ] . _oRndSeed ) ;
Objects [ i ] . _oVar1 = FindValidShrine ( ) ;
OperateShrine ( pnum , i , sType ) ;
Objects [ i ] . _oAnimFrame = 3 ;
Objects [ i ] . _oAnimFlag = 0 ;
force_redraw = 255 ;
}
bool OperateFountains ( int pnum , int i )
{
auto & player = Players [ pnum ] ;
bool applied = false ;
SetRndSeed ( Objects [ i ] . _oRndSeed ) ;
switch ( Objects [ i ] . _otype ) {
case OBJ_BLOODFTN :
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return false ;
if ( player . _pHitPoints < player . _pMaxHP ) {
PlaySfxLoc ( LS_FOUNTAIN , Objects [ i ] . position ) ;
player . _pHitPoints + = 64 ;
player . _pHPBase + = 64 ;
if ( player . _pHitPoints > player . _pMaxHP ) {
player . _pHitPoints = player . _pMaxHP ;
player . _pHPBase = player . _pMaxHPBase ;
}
applied = true ;
} else
PlaySfxLoc ( LS_FOUNTAIN , Objects [ i ] . position ) ;
break ;
case OBJ_PURIFYINGFTN :
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return false ;
if ( player . _pMana < player . _pMaxMana ) {
PlaySfxLoc ( LS_FOUNTAIN , Objects [ i ] . position ) ;
player . _pMana + = 64 ;
player . _pManaBase + = 64 ;
if ( player . _pMana > player . _pMaxMana ) {
player . _pMana = player . _pMaxMana ;
player . _pManaBase = player . _pMaxManaBase ;
}
applied = true ;
} else
PlaySfxLoc ( LS_FOUNTAIN , Objects [ i ] . position ) ;
break ;
case OBJ_MURKYFTN :
if ( Objects [ i ] . _oSelFlag = = 0 )
break ;
if ( ! deltaload )
PlaySfxLoc ( LS_FOUNTAIN , Objects [ i ] . position ) ;
Objects [ i ] . _oSelFlag = 0 ;
if ( deltaload )
return false ;
AddMissile (
player . position . tile ,
player . position . tile ,
player . _pdir ,
MIS_INFRA ,
TARGET_PLAYERS ,
pnum ,
0 ,
2 * leveltype ) ;
applied = true ;
if ( pnum = = MyPlayerId )
NetSendCmdParam1 ( false , CMD_OPERATEOBJ , i ) ;
break ;
case OBJ_TEARFTN : {
if ( Objects [ i ] . _oSelFlag = = 0 )
break ;
if ( ! deltaload )
PlaySfxLoc ( LS_FOUNTAIN , Objects [ i ] . position ) ;
Objects [ i ] . _oSelFlag = 0 ;
if ( deltaload )
return false ;
if ( pnum ! = MyPlayerId )
return false ;
int fromStat = GenerateRnd ( 4 ) ;
int toStat = abs ( GenerateRnd ( 3 ) ) ;
if ( toStat > = fromStat )
toStat + + ;
std : : pair < int , int > alterations [ ] = { { fromStat , - 1 } , { toStat , 1 } } ;
for ( auto alteration : alterations ) {
switch ( alteration . first ) {
case 0 :
ModifyPlrStr ( pnum , alteration . second ) ;
break ;
case 1 :
ModifyPlrMag ( pnum , alteration . second ) ;
break ;
case 2 :
ModifyPlrDex ( pnum , alteration . second ) ;
break ;
case 3 :
ModifyPlrVit ( pnum , alteration . second ) ;
break ;
}
}
CheckStats ( player ) ;
applied = true ;
if ( pnum = = MyPlayerId )
NetSendCmdParam1 ( false , CMD_OPERATEOBJ , i ) ;
} break ;
default :
break ;
}
force_redraw = 255 ;
return applied ;
}
void OperateWeaponRack ( int pnum , int i , bool sendmsg )
{
int weaponType ;
if ( Objects [ i ] . _oSelFlag = = 0 )
return ;
SetRndSeed ( Objects [ i ] . _oRndSeed ) ;
switch ( GenerateRnd ( 4 ) + ITYPE_SWORD ) {
case ITYPE_SWORD :
weaponType = ITYPE_SWORD ;
break ;
case ITYPE_AXE :
weaponType = ITYPE_AXE ;
break ;
case ITYPE_BOW :
weaponType = ITYPE_BOW ;
break ;
case ITYPE_MACE :
weaponType = ITYPE_MACE ;
break ;
}
Objects [ i ] . _oSelFlag = 0 ;
Objects [ i ] . _oAnimFrame + + ;
if ( deltaload )
return ;
CreateTypeItem ( Objects [ i ] . position , leveltype > 1 , weaponType , IMISC_NONE , sendmsg , false ) ;
if ( pnum = = MyPlayerId )
NetSendCmdParam1 ( false , CMD_OPERATEOBJ , i ) ;
}
bool OperateNakrulBook ( int s )
{
switch ( s ) {
case 6 :
NaKrulTomeSequence = 1 ;
break ;
case 7 :
if ( NaKrulTomeSequence = = 1 ) {
NaKrulTomeSequence = 2 ;
} else {
NaKrulTomeSequence = 0 ;
}
break ;
case 8 :
if ( NaKrulTomeSequence = = 2 )
return true ;
NaKrulTomeSequence = 0 ;
break ;
}
return false ;
}
void OperateStoryBook ( int pnum , int i )
{
if ( Objects [ i ] . _oSelFlag = = 0 | | deltaload | | qtextflag | | pnum ! = MyPlayerId ) {
return ;
}
Objects [ i ] . _oAnimFrame = Objects [ i ] . _oVar4 ;
PlaySfxLoc ( IS_ISCROL , Objects [ i ] . position ) ;
auto msg = static_cast < _speech_id > ( Objects [ i ] . _oVar2 ) ;
if ( Objects [ i ] . _oVar8 ! = 0 & & currlevel = = 24 ) {
if ( ! IsUberLeverActivated & & Quests [ Q_NAKRUL ] . _qactive ! = QUEST_DONE & & OperateNakrulBook ( Objects [ i ] . _oVar8 ) ) {
NetSendCmd ( false , CMD_NAKRUL ) ;
return ;
}
} else if ( currlevel > = 21 ) {
Quests [ Q_NAKRUL ] . _qactive = QUEST_ACTIVE ;
Quests [ Q_NAKRUL ] . _qlog = true ;
Quests [ Q_NAKRUL ] . _qmsg = msg ;
}
InitQTextMsg ( msg ) ;
NetSendCmdParam1 ( false , CMD_OPERATEOBJ , i ) ;
}
void OperateLazStand ( int pnum , int i )
{
if ( ActiveItemCount > = MAXITEMS ) {
return ;
}
if ( Objects [ i ] . _oSelFlag = = 0 | | deltaload | | qtextflag | | pnum ! = MyPlayerId ) {
return ;
}
Objects [ i ] . _oAnimFrame + + ;
Objects [ i ] . _oSelFlag = 0 ;
Point pos = GetSuperItemLoc ( Objects [ i ] . position ) ;
SpawnQuestItem ( IDI_LAZSTAFF , pos , 0 , 0 ) ;
}
void SyncOpL1Door ( int pnum , int cmd , int i )
{
if ( pnum = = MyPlayerId )
return ;
bool doSync = false ;
if ( cmd = = CMD_OPENDOOR & & Objects [ i ] . _oVar4 = = 0 )
doSync = true ;
if ( cmd = = CMD_CLOSEDOOR & & Objects [ i ] . _oVar4 = = 1 )
doSync = true ;
if ( ! doSync )
return ;
if ( Objects [ i ] . _otype = = OBJ_L1LDOOR )
OperateL1LDoor ( - 1 , i , false ) ;
if ( Objects [ i ] . _otype = = OBJ_L1RDOOR )
OperateL1RDoor ( - 1 , i , false ) ;
}
void SyncOpL2Door ( int pnum , int cmd , int i )
{
if ( pnum = = MyPlayerId )
return ;
bool doSync = false ;
if ( cmd = = CMD_OPENDOOR & & Objects [ i ] . _oVar4 = = 0 )
doSync = true ;
if ( cmd = = CMD_CLOSEDOOR & & Objects [ i ] . _oVar4 = = 1 )
doSync = true ;
if ( ! doSync )
return ;
if ( Objects [ i ] . _otype = = OBJ_L2LDOOR )
OperateL2LDoor ( - 1 , i , false ) ;
if ( Objects [ i ] . _otype = = OBJ_L2RDOOR )
OperateL2RDoor ( - 1 , i , false ) ;
}
void SyncOpL3Door ( int pnum , int cmd , int i )
{
if ( pnum = = MyPlayerId )
return ;
bool doSync = false ;
if ( cmd = = CMD_OPENDOOR & & Objects [ i ] . _oVar4 = = 0 )
doSync = true ;
if ( cmd = = CMD_CLOSEDOOR & & Objects [ i ] . _oVar4 = = 1 )
doSync = true ;
if ( ! doSync )
return ;
if ( Objects [ i ] . _otype = = OBJ_L3LDOOR )
OperateL3LDoor ( - 1 , i , false ) ;
if ( Objects [ i ] . _otype = = OBJ_L3RDOOR )
OperateL3RDoor ( - 1 , i , false ) ;
}
/**
* @ brief Checks if all active crux objects of the given type have been broken .
*
* Called by BreakCrux and SyncCrux to see if the linked map area needs to be updated . In practice I think this is
* always true when called by BreakCrux as there * should * only be one instance of each crux with a given _oVar8 value ?
*
* @ param cruxType Discriminator / type ( _oVar8 value ) of the crux object which is currently changing state
* @ return true if all active cruxes of that type on the level are broken , false if at least one remains unbroken
*/
bool AreAllCruxesOfTypeBroken ( int cruxType )
{
for ( int j = 0 ; j < ActiveObjectCount ; j + + ) {
const auto & testObject = Objects [ ActiveObjects [ j ] ] ;
if ( IsNoneOf ( testObject . _otype , OBJ_CRUX1 , OBJ_CRUX2 , OBJ_CRUX3 ) )
continue ; // Not a Crux object, keep searching
if ( cruxType ! = testObject . _oVar8 | | testObject . _oBreak = = - 1 )
continue ; // Found either a different crux or a previously broken crux, keep searching
// Found an unbroken crux of this type
return false ;
}
return true ;
}
void BreakCrux ( ObjectStruct & crux )
{
crux . _oAnimFlag = 1 ;
crux . _oAnimFrame = 1 ;
crux . _oAnimDelay = 1 ;
crux . _oSolidFlag = true ;
crux . _oMissFlag = true ;
crux . _oBreak = - 1 ;
crux . _oSelFlag = 0 ;
if ( ! AreAllCruxesOfTypeBroken ( crux . _oVar8 ) )
return ;
if ( ! deltaload )
PlaySfxLoc ( IS_LEVER , crux . position ) ;
ObjChangeMap ( crux . _oVar1 , crux . _oVar2 , crux . _oVar3 , crux . _oVar4 ) ;
}
void BreakBarrel ( int pnum , int i , int dam , bool forcebreak , bool sendmsg )
{
if ( Objects [ i ] . _oSelFlag = = 0 )
return ;
if ( forcebreak ) {
Objects [ i ] . _oVar1 = 0 ;
} else {
Objects [ i ] . _oVar1 - = dam ;
if ( pnum ! = MyPlayerId & & Objects [ i ] . _oVar1 < = 0 )
Objects [ i ] . _oVar1 = 1 ;
}
if ( Objects [ i ] . _oVar1 > 0 ) {
if ( deltaload )
return ;
PlaySfxLoc ( IS_IBOW , Objects [ i ] . position ) ;
return ;
}
Objects [ i ] . _oVar1 = 0 ;
Objects [ i ] . _oAnimFlag = 1 ;
Objects [ i ] . _oAnimFrame = 1 ;
Objects [ i ] . _oAnimDelay = 1 ;
Objects [ i ] . _oSolidFlag = false ;
Objects [ i ] . _oMissFlag = true ;
Objects [ i ] . _oBreak = - 1 ;
Objects [ i ] . _oSelFlag = 0 ;
Objects [ i ] . _oPreFlag = true ;
if ( deltaload ) {
Objects [ i ] . _oAnimFrame = Objects [ i ] . _oAnimLen ;
Objects [ i ] . _oAnimCnt = 0 ;
Objects [ i ] . _oAnimDelay = 1000 ;
return ;
}
if ( Objects [ i ] . _otype = = OBJ_BARRELEX ) {
if ( currlevel > = 21 & & currlevel < = 24 )
PlaySfxLoc ( IS_POPPOP3 , Objects [ i ] . position ) ;
else if ( currlevel > = 17 & & currlevel < = 20 )
PlaySfxLoc ( IS_POPPOP8 , Objects [ i ] . position ) ;
else
PlaySfxLoc ( IS_BARLFIRE , Objects [ i ] . position ) ;
for ( int yp = Objects [ i ] . position . y - 1 ; yp < = Objects [ i ] . position . y + 1 ; yp + + ) {
for ( int xp = Objects [ i ] . position . x - 1 ; xp < = Objects [ i ] . position . x + 1 ; xp + + ) {
if ( dMonster [ xp ] [ yp ] > 0 )
MonsterTrapHit ( dMonster [ xp ] [ yp ] - 1 , 1 , 4 , 0 , MIS_FIREBOLT , false ) ;
bool unused ;
if ( dPlayer [ xp ] [ yp ] > 0 )
PlayerMHit ( dPlayer [ xp ] [ yp ] - 1 , nullptr , 0 , 8 , 16 , MIS_FIREBOLT , false , 0 , & unused ) ;
if ( dObject [ xp ] [ yp ] > 0 ) {
int oi = dObject [ xp ] [ yp ] - 1 ;
if ( Objects [ oi ] . _otype = = OBJ_BARRELEX & & Objects [ oi ] . _oBreak ! = - 1 )
BreakBarrel ( pnum , oi , dam , true , sendmsg ) ;
}
}
}
} else {
if ( currlevel > = 21 & & currlevel < = 24 )
PlaySfxLoc ( IS_POPPOP2 , Objects [ i ] . position ) ;
else if ( currlevel > = 17 & & currlevel < = 20 )
PlaySfxLoc ( IS_POPPOP5 , Objects [ i ] . position ) ;
else
PlaySfxLoc ( IS_BARREL , Objects [ i ] . position ) ;
SetRndSeed ( Objects [ i ] . _oRndSeed ) ;
if ( Objects [ i ] . _oVar2 < = 1 ) {
if ( Objects [ i ] . _oVar3 = = 0 )
CreateRndUseful ( Objects [ i ] . position , sendmsg ) ;
else
CreateRndItem ( Objects [ i ] . position , false , sendmsg , false ) ;
}
if ( Objects [ i ] . _oVar2 > = 8 )
SpawnSkeleton ( Objects [ i ] . _oVar4 , Objects [ i ] . position ) ;
}
if ( pnum = = MyPlayerId )
NetSendCmdParam2 ( false , CMD_BREAKOBJ , pnum , i ) ;
}
void SyncCrux ( const ObjectStruct & crux )
{
if ( AreAllCruxesOfTypeBroken ( crux . _oVar8 ) )
ObjChangeMap ( crux . _oVar1 , crux . _oVar2 , crux . _oVar3 , crux . _oVar4 ) ;
}
void SyncLever ( const ObjectStruct & lever )
{
if ( lever . _oSelFlag ! = 0 )
return ;
ObjChangeMap ( lever . _oVar1 , lever . _oVar2 , lever . _oVar3 , lever . _oVar4 ) ;
}
void SyncQSTLever ( const ObjectStruct & qstLever )
{
if ( qstLever . _oAnimFrame = = qstLever . _oVar6 ) {
ObjChangeMapResync ( qstLever . _oVar1 , qstLever . _oVar2 , qstLever . _oVar3 , qstLever . _oVar4 ) ;
if ( qstLever . _otype = = OBJ_BLINDBOOK ) {
auto tren = TransVal ;
TransVal = 9 ;
DRLG_MRectTrans ( qstLever . _oVar1 , qstLever . _oVar2 , qstLever . _oVar3 , qstLever . _oVar4 ) ;
TransVal = tren ;
}
}
}
void SyncPedestal ( const ObjectStruct & pedestal , Point origin , int width )
{
if ( pedestal . _oVar6 = = 1 )
ObjChangeMapResync ( origin . x , origin . y + 3 , origin . x + 2 , origin . y + 7 ) ;
if ( pedestal . _oVar6 = = 2 ) {
ObjChangeMapResync ( origin . x , origin . y + 3 , origin . x + 2 , origin . y + 7 ) ;
ObjChangeMapResync ( origin . x + 6 , origin . y + 3 , origin . x + width , origin . y + 7 ) ;
}
if ( pedestal . _oVar6 = = 3 ) {
ObjChangeMapResync ( pedestal . _oVar1 , pedestal . _oVar2 , pedestal . _oVar3 , pedestal . _oVar4 ) ;
LoadMapObjs ( " Levels \\ L2Data \\ Blood2.DUN " , origin * 2 ) ;
}
}
void SyncL1Doors ( ObjectStruct & door )
{
if ( door . _oVar4 = = 0 ) {
door . _oMissFlag = false ;
return ;
}
door . _oMissFlag = true ;
door . _oSelFlag = 2 ;
bool isLeftDoor = door . _otype = = _object_id : : OBJ_L1LDOOR ; // otherwise the door is type OBJ_L1RDOOR
if ( currlevel < 17 ) {
if ( isLeftDoor ) {
ObjSetMicro ( door . position , door . _oVar1 = = 214 ? 408 : 393 ) ;
dSpecial [ door . position . x ] [ door . position . y ] = 7 ;
SetDoorPiece ( door . position + Direction : : DIR_NW ) ;
DoorSet ( door . position + Direction : : DIR_NE , isLeftDoor ) ;
} else {
ObjSetMicro ( door . position , 395 ) ;
dSpecial [ door . position . x ] [ door . position . y ] = 8 ;
SetDoorPiece ( door . position + Direction : : DIR_NE ) ;
DoorSet ( door . position + Direction : : DIR_NW , isLeftDoor ) ;
}
} else {
if ( isLeftDoor ) {
ObjSetMicro ( door . position , 206 ) ;
dSpecial [ door . position . x ] [ door . position . y ] = 1 ;
SetDoorPiece ( door . position + Direction : : DIR_NW ) ;
DoorSet ( door . position + Direction : : DIR_NE , isLeftDoor ) ;
} else {
ObjSetMicro ( door . position , 209 ) ;
dSpecial [ door . position . x ] [ door . position . y ] = 2 ;
SetDoorPiece ( door . position + Direction : : DIR_NE ) ;
DoorSet ( door . position + Direction : : DIR_NW , isLeftDoor ) ;
}
}
}
void SyncL2Doors ( ObjectStruct & door )
{
door . _oMissFlag = door . _oVar4 ! = 0 ;
door . _oSelFlag = 2 ;
bool isLeftDoor = door . _otype = = _object_id : : OBJ_L2LDOOR ; // otherwise the door is type OBJ_L2RDOOR
switch ( door . _oVar4 ) {
case 0 :
ObjSetMicro ( door . position , isLeftDoor ? 538 : 540 ) ;
dSpecial [ door . position . x ] [ door . position . y ] = 0 ;
break ;
case 1 :
case 2 :
ObjSetMicro ( door . position , isLeftDoor ? 13 : 17 ) ;
dSpecial [ door . position . x ] [ door . position . y ] = isLeftDoor ? 5 : 6 ;
break ;
}
}
void SyncL3Doors ( ObjectStruct & door )
{
door . _oMissFlag = true ;
door . _oSelFlag = 2 ;
bool isLeftDoor = door . _otype = = _object_id : : OBJ_L3LDOOR ; // otherwise the door is type OBJ_L3RDOOR
switch ( door . _oVar4 ) {
case 0 :
ObjSetMicro ( door . position , isLeftDoor ? 531 : 534 ) ;
break ;
case 1 :
case 2 :
ObjSetMicro ( door . position , isLeftDoor ? 538 : 541 ) ;
break ;
}
}
} // namespace
void InitObjectGFX ( )
{
bool fileload [ 56 ] = { } ;
int lvl = currlevel ;
if ( currlevel > = 21 & & currlevel < = 24 )
lvl - = 20 ;
else if ( currlevel > = 17 & & currlevel < = 20 )
lvl - = 8 ;
for ( int i = 0 ; AllObjects [ i ] . oload ! = - 1 ; i + + ) {
if ( AllObjects [ i ] . oload = = 1
& & lvl > = AllObjects [ i ] . ominlvl
& & lvl < = AllObjects [ i ] . omaxlvl ) {
fileload [ AllObjects [ i ] . ofindex ] = true ;
}
if ( AllObjects [ i ] . otheme ! = THEME_NONE ) {
for ( int j = 0 ; j < numthemes ; j + + ) {
if ( themes [ j ] . ttype = = AllObjects [ i ] . otheme )
fileload [ AllObjects [ i ] . ofindex ] = true ;
}
}
if ( AllObjects [ i ] . oquest ! = - 1 ) {
if ( Quests [ AllObjects [ i ] . oquest ] . IsAvailable ( ) )
fileload [ AllObjects [ i ] . ofindex ] = true ;
}
}
for ( int i = OFILE_L1BRAZ ; i < = OFILE_LZSTAND ; i + + ) {
if ( fileload [ i ] ) {
ObjFileList [ numobjfiles ] = static_cast < object_graphic_id > ( i ) ;
char filestr [ 32 ] ;
sprintf ( filestr , " Objects \\ %s.CEL " , ObjMasterLoadList [ i ] ) ;
if ( currlevel > = 17 & & currlevel < 21 )
sprintf ( filestr , " Objects \\ %s.CEL " , ObjHiveLoadList [ i ] ) ;
else if ( currlevel > = 21 )
sprintf ( filestr , " Objects \\ %s.CEL " , ObjCryptLoadList [ i ] ) ;
pObjCels [ numobjfiles ] = LoadFileInMem ( filestr ) ;
numobjfiles + + ;
}
}
}
void FreeObjectGFX ( )
{
for ( int i = 0 ; i < numobjfiles ; i + + ) {
pObjCels [ i ] = nullptr ;
}
numobjfiles = 0 ;
}
void AddL1Objs ( int x1 , int y1 , int x2 , int y2 )
{
for ( int j = y1 ; j < y2 ; j + + ) {
for ( int i = x1 ; i < x2 ; i + + ) {
int pn = dPiece [ i ] [ j ] ;
if ( pn = = 270 )
AddObject ( OBJ_L1LIGHT , { i , j } ) ;
if ( pn = = 44 | | pn = = 51 | | pn = = 214 )
AddObject ( OBJ_L1LDOOR , { i , j } ) ;
if ( pn = = 46 | | pn = = 56 )
AddObject ( OBJ_L1RDOOR , { i , j } ) ;
}
}
}
void AddL2Objs ( int x1 , int y1 , int x2 , int y2 )
{
for ( int j = y1 ; j < y2 ; j + + ) {
for ( int i = x1 ; i < x2 ; i + + ) {
int pn = dPiece [ i ] [ j ] ;
if ( pn = = 13 | | pn = = 541 )
AddObject ( OBJ_L2LDOOR , { i , j } ) ;
if ( pn = = 17 | | pn = = 542 )
AddObject ( OBJ_L2RDOOR , { i , j } ) ;
}
}
}
void AddSlainHero ( )
{
int x ;
int y ;
GetRndObjLoc ( 5 , & x , & y ) ;
AddObject ( OBJ_SLAINHERO , { x + 2 , y + 2 } ) ;
}
void InitObjects ( )
{
ClrAllObjects ( ) ;
NaKrulTomeSequence = 0 ;
if ( currlevel = = 16 ) {
AddDiabObjs ( ) ;
} else {
ApplyObjectLighting = true ;
AdvanceRndSeed ( ) ;
if ( currlevel = = 9 & & ! gbIsMultiplayer )
AddSlainHero ( ) ;
if ( currlevel = = Quests [ Q_MUSHROOM ] . _qlevel & & Quests [ Q_MUSHROOM ] . _qactive = = QUEST_INIT )
AddMushPatch ( ) ;
if ( currlevel = = 4 | | currlevel = = 8 | | currlevel = = 12 )
AddStoryBooks ( ) ;
if ( currlevel = = 21 ) {
AddCryptStoryBook ( 1 ) ;
} else if ( currlevel = = 22 ) {
AddCryptStoryBook ( 2 ) ;
AddCryptStoryBook ( 3 ) ;
} else if ( currlevel = = 23 ) {
AddCryptStoryBook ( 4 ) ;
AddCryptStoryBook ( 5 ) ;
}
if ( currlevel = = 24 ) {
AddNakrulGate ( ) ;
}
if ( leveltype = = DTYPE_CATHEDRAL ) {
if ( Quests [ Q_BUTCHER ] . IsAvailable ( ) )
AddTortures ( ) ;
if ( Quests [ Q_PWATER ] . IsAvailable ( ) )
AddCandles ( ) ;
if ( Quests [ Q_LTBANNER ] . IsAvailable ( ) )
AddObject ( OBJ_SIGNCHEST , { 2 * setpc_x + 26 , 2 * setpc_y + 19 } ) ;
InitRndLocBigObj ( 10 , 15 , OBJ_SARC ) ;
if ( currlevel > = 21 )
AddCryptObjects ( 0 , 0 , MAXDUNX , MAXDUNY ) ;
else
AddL1Objs ( 0 , 0 , MAXDUNX , MAXDUNY ) ;
InitRndBarrels ( ) ;
}
if ( leveltype = = DTYPE_CATACOMBS ) {
if ( Quests [ Q_ROCK ] . IsAvailable ( ) )
InitRndLocObj5x5 ( 1 , 1 , OBJ_STAND ) ;
if ( Quests [ Q_SCHAMB ] . IsAvailable ( ) )
InitRndLocObj5x5 ( 1 , 1 , OBJ_BOOK2R ) ;
AddL2Objs ( 0 , 0 , MAXDUNX , MAXDUNY ) ;
AddL2Torches ( ) ;
if ( Quests [ Q_BLIND ] . IsAvailable ( ) ) {
_speech_id spId ;
switch ( Players [ MyPlayerId ] . _pClass ) {
case HeroClass : : Warrior :
spId = TEXT_BLINDING ;
break ;
case HeroClass : : Rogue :
spId = TEXT_RBLINDING ;
break ;
case HeroClass : : Sorcerer :
spId = TEXT_MBLINDING ;
break ;
case HeroClass : : Monk :
spId = TEXT_HBLINDING ;
break ;
case HeroClass : : Bard :
spId = TEXT_BBLINDING ;
break ;
case HeroClass : : Barbarian :
spId = TEXT_BLINDING ;
break ;
}
Quests [ Q_BLIND ] . _qmsg = spId ;
AddBookLever ( { { setpc_x , setpc_y } , { setpc_w + 1 , setpc_h + 1 } } , spId ) ;
LoadMapObjs ( " Levels \\ L2Data \\ Blind2.DUN " , { 2 * setpc_x , 2 * setpc_y } ) ;
}
if ( Quests [ Q_BLOOD ] . IsAvailable ( ) ) {
_speech_id spId ;
switch ( Players [ MyPlayerId ] . _pClass ) {
case HeroClass : : Warrior :
spId = TEXT_BLOODY ;
break ;
case HeroClass : : Rogue :
spId = TEXT_RBLOODY ;
break ;
case HeroClass : : Sorcerer :
spId = TEXT_MBLOODY ;
break ;
case HeroClass : : Monk :
spId = TEXT_HBLOODY ;
break ;
case HeroClass : : Bard :
spId = TEXT_BBLOODY ;
break ;
case HeroClass : : Barbarian :
spId = TEXT_BLOODY ;
break ;
}
Quests [ Q_BLOOD ] . _qmsg = spId ;
AddBookLever ( { { setpc_x , setpc_y + 3 } , { 2 , 4 } } , spId ) ;
AddObject ( OBJ_PEDISTAL , { 2 * setpc_x + 25 , 2 * setpc_y + 32 } ) ;
}
InitRndBarrels ( ) ;
}
if ( leveltype = = DTYPE_CAVES ) {
AddL3Objs ( 0 , 0 , MAXDUNX , MAXDUNY ) ;
InitRndBarrels ( ) ;
}
if ( leveltype = = DTYPE_HELL ) {
if ( Quests [ Q_WARLORD ] . IsAvailable ( ) ) {
_speech_id spId ;
switch ( Players [ MyPlayerId ] . _pClass ) {
case HeroClass : : Warrior :
spId = TEXT_BLOODWAR ;
break ;
case HeroClass : : Rogue :
spId = TEXT_RBLOODWAR ;
break ;
case HeroClass : : Sorcerer :
spId = TEXT_MBLOODWAR ;
break ;
case HeroClass : : Monk :
spId = TEXT_HBLOODWAR ;
break ;
case HeroClass : : Bard :
spId = TEXT_BBLOODWAR ;
break ;
case HeroClass : : Barbarian :
spId = TEXT_BLOODWAR ;
break ;
}
Quests [ Q_WARLORD ] . _qmsg = spId ;
AddBookLever ( { { setpc_x , setpc_y } , { setpc_w , setpc_h } } , spId ) ;
LoadMapObjs ( " Levels \\ L4Data \\ Warlord.DUN " , { 2 * setpc_x , 2 * setpc_y } ) ;
}
if ( Quests [ Q_BETRAYER ] . IsAvailable ( ) & & ! gbIsMultiplayer )
AddLazStand ( ) ;
InitRndBarrels ( ) ;
AddL4Goodies ( ) ;
}
InitRndLocObj ( 5 , 10 , OBJ_CHEST1 ) ;
InitRndLocObj ( 3 , 6 , OBJ_CHEST2 ) ;
InitRndLocObj ( 1 , 5 , OBJ_CHEST3 ) ;
if ( leveltype ! = DTYPE_HELL )
AddObjTraps ( ) ;
if ( leveltype > DTYPE_CATHEDRAL )
AddChestTraps ( ) ;
ApplyObjectLighting = false ;
}
}
void SetMapObjects ( const uint16_t * dunData , int startx , int starty )
{
bool filesLoaded [ 56 ] ;
char filestr [ 32 ] ;
ClrAllObjects ( ) ;
for ( auto & fileLoaded : filesLoaded )
fileLoaded = false ;
ApplyObjectLighting = true ;
for ( int i = 0 ; AllObjects [ i ] . oload ! = - 1 ; i + + ) {
if ( AllObjects [ i ] . oload = = 1 & & leveltype = = AllObjects [ i ] . olvltype )
filesLoaded [ AllObjects [ i ] . ofindex ] = true ;
}
int width = SDL_SwapLE16 ( dunData [ 0 ] ) ;
int height = SDL_SwapLE16 ( dunData [ 1 ] ) ;
int layer2Offset = 2 + width * height ;
// The rest of the layers are at dPiece scale
width * = 2 ;
height * = 2 ;
const uint16_t * objectLayer = & dunData [ layer2Offset + width * height * 2 ] ;
for ( int j = 0 ; j < height ; j + + ) {
for ( int i = 0 ; i < width ; i + + ) {
uint8_t objectId = SDL_SwapLE16 ( objectLayer [ j * width + i ] ) ;
if ( objectId ! = 0 ) {
filesLoaded [ AllObjects [ ObjTypeConv [ objectId ] ] . ofindex ] = true ;
}
}
}
for ( int i = OFILE_L1BRAZ ; i < = OFILE_LZSTAND ; i + + ) {
if ( ! filesLoaded [ i ] )
continue ;
ObjFileList [ numobjfiles ] = ( object_graphic_id ) i ;
sprintf ( filestr , " Objects \\ %s.CEL " , ObjMasterLoadList [ i ] ) ;
pObjCels [ numobjfiles ] = LoadFileInMem ( filestr ) ;
numobjfiles + + ;
}
for ( int j = 0 ; j < height ; j + + ) {
for ( int i = 0 ; i < width ; i + + ) {
uint8_t objectId = SDL_SwapLE16 ( objectLayer [ j * width + i ] ) ;
if ( objectId ! = 0 ) {
AddObject ( ObjTypeConv [ objectId ] , { startx + 16 + i , starty + 16 + j } ) ;
}
}
}
ApplyObjectLighting = false ;
}
void AddObject ( _object_id objType , Point objPos )
{
if ( ActiveObjectCount > = MAXOBJECTS )
return ;
int oi = AvailableObjects [ 0 ] ;
AvailableObjects [ 0 ] = AvailableObjects [ MAXOBJECTS - 1 - ActiveObjectCount ] ;
ActiveObjects [ ActiveObjectCount ] = oi ;
dObject [ objPos . x ] [ objPos . y ] = oi + 1 ;
SetupObject ( oi , objPos , objType ) ;
switch ( objType ) {
case OBJ_L1LIGHT :
case OBJ_SKFIRE :
case OBJ_CANDLE1 :
case OBJ_CANDLE2 :
case OBJ_BOOKCANDLE :
AddObjectLight ( oi , 5 ) ;
break ;
case OBJ_STORYCANDLE :
AddObjectLight ( oi , 3 ) ;
break ;
case OBJ_TORCHL :
case OBJ_TORCHR :
case OBJ_TORCHL2 :
case OBJ_TORCHR2 :
AddObjectLight ( oi , 8 ) ;
break ;
case OBJ_L1LDOOR :
case OBJ_L1RDOOR :
AddL1Door ( oi , objPos , objType ) ;
break ;
case OBJ_L2LDOOR :
case OBJ_L2RDOOR :
AddL2Door ( oi , objPos , objType ) ;
break ;
case OBJ_L3LDOOR :
case OBJ_L3RDOOR :
AddL3Door ( oi , objPos , objType ) ;
break ;
case OBJ_BOOK2R :
Objects [ oi ] . InitializeBook ( { { setpc_x , setpc_y } , { setpc_w + 1 , setpc_h + 1 } } ) ;
break ;
case OBJ_CHEST1 :
case OBJ_CHEST2 :
case OBJ_CHEST3 :
AddChest ( oi , objType ) ;
break ;
case OBJ_TCHEST1 :
case OBJ_TCHEST2 :
case OBJ_TCHEST3 :
AddChest ( oi , objType ) ;
Objects [ oi ] . _oTrapFlag = true ;
if ( leveltype = = DTYPE_CATACOMBS ) {
Objects [ oi ] . _oVar4 = GenerateRnd ( 2 ) ;
} else {
Objects [ oi ] . _oVar4 = GenerateRnd ( 3 ) ;
}
break ;
case OBJ_SARC :
AddSarc ( oi ) ;
break ;
case OBJ_FLAMEHOLE :
AddFlameTrap ( oi ) ;
break ;
case OBJ_FLAMELVR :
AddFlameLvr ( oi ) ;
break ;
case OBJ_WATER :
Objects [ oi ] . _oAnimFrame = 1 ;
break ;
case OBJ_TRAPL :
case OBJ_TRAPR :
AddTrap ( oi ) ;
break ;
case OBJ_BARREL :
case OBJ_BARRELEX :
AddBarrel ( oi , objType ) ;
break ;
case OBJ_SHRINEL :
case OBJ_SHRINER :
AddShrine ( oi ) ;
break ;
case OBJ_BOOKCASEL :
case OBJ_BOOKCASER :
AddBookcase ( oi ) ;
break ;
case OBJ_SKELBOOK :
case OBJ_BOOKSTAND :
AddBookstand ( oi ) ;
break ;
case OBJ_BLOODFTN :
AddBloodFtn ( oi ) ;
break ;
case OBJ_DECAP :
AddDecap ( oi ) ;
break ;
case OBJ_PURIFYINGFTN :
AddPurifyingFountain ( oi ) ;
break ;
case OBJ_ARMORSTAND :
case OBJ_WARARMOR :
AddArmorStand ( oi ) ;
break ;
case OBJ_GOATSHRINE :
AddGoatShrine ( oi ) ;
break ;
case OBJ_CAULDRON :
AddCauldron ( oi ) ;
break ;
case OBJ_MURKYFTN :
AddMurkyFountain ( oi ) ;
break ;
case OBJ_TEARFTN :
AddTearFountain ( oi ) ;
break ;
case OBJ_BOOK2L :
AddVilebook ( oi ) ;
break ;
case OBJ_MCIRCLE1 :
case OBJ_MCIRCLE2 :
AddMagicCircle ( oi ) ;
break ;
case OBJ_STORYBOOK :
AddStoryBook ( oi ) ;
break ;
case OBJ_BCROSS :
case OBJ_TBCROSS :
AddBrnCross ( oi ) ;
AddObjectLight ( oi , 5 ) ;
break ;
case OBJ_PEDISTAL :
AddPedistal ( oi ) ;
break ;
case OBJ_WARWEAP :
case OBJ_WEAPONRACK :
AddWeaponRack ( oi ) ;
break ;
case OBJ_TNUDEM2 :
AddTorturedBody ( oi ) ;
break ;
default :
break ;
}
ActiveObjectCount + + ;
}
void Obj_Trap ( int i )
{
if ( Objects [ i ] . _oVar4 ! = 0 )
return ;
int oti = dObject [ Objects [ i ] . _oVar1 ] [ Objects [ i ] . _oVar2 ] - 1 ;
switch ( Objects [ oti ] . _otype ) {
case OBJ_L1LDOOR :
case OBJ_L1RDOOR :
case OBJ_L2LDOOR :
case OBJ_L2RDOOR :
case OBJ_L3LDOOR :
case OBJ_L3RDOOR :
if ( Objects [ oti ] . _oVar4 = = 0 )
return ;
break ;
case OBJ_LEVER :
case OBJ_CHEST1 :
case OBJ_CHEST2 :
case OBJ_CHEST3 :
case OBJ_SWITCHSKL :
case OBJ_SARC :
if ( Objects [ oti ] . _oSelFlag ! = 0 )
return ;
break ;
default :
return ;
}
Objects [ i ] . _oVar4 = 1 ;
Point target = Objects [ oti ] . position ;
for ( int y = target . y - 1 ; y < = Objects [ oti ] . position . y + 1 ; y + + ) {
for ( int x = Objects [ oti ] . position . x - 1 ; x < = Objects [ oti ] . position . x + 1 ; x + + ) {
if ( dPlayer [ x ] [ y ] ! = 0 ) {
target . x = x ;
target . y = y ;
}
}
}
if ( ! deltaload ) {
Direction dir = GetDirection ( Objects [ i ] . position , target ) ;
AddMissile ( Objects [ i ] . position , target , dir , static_cast < missile_id > ( Objects [ i ] . _oVar3 ) , TARGET_PLAYERS , - 1 , 0 , 0 ) ;
PlaySfxLoc ( IS_TRAP , Objects [ oti ] . position ) ;
}
Objects [ oti ] . _oTrapFlag = false ;
}
void ProcessObjects ( )
{
for ( int i = 0 ; i < ActiveObjectCount ; + + i ) {
int oi = ActiveObjects [ i ] ;
switch ( Objects [ oi ] . _otype ) {
case OBJ_L1LIGHT :
UpdateObjectLight ( oi , 10 ) ;
break ;
case OBJ_SKFIRE :
case OBJ_CANDLE2 :
case OBJ_BOOKCANDLE :
UpdateObjectLight ( oi , 5 ) ;
break ;
case OBJ_STORYCANDLE :
UpdateObjectLight ( oi , 3 ) ;
break ;
case OBJ_CRUX1 :
case OBJ_CRUX2 :
case OBJ_CRUX3 :
case OBJ_BARREL :
case OBJ_BARRELEX :
case OBJ_SHRINEL :
case OBJ_SHRINER :
ObjectStopAnim ( oi ) ;
break ;
case OBJ_L1LDOOR :
case OBJ_L1RDOOR :
case OBJ_L2LDOOR :
case OBJ_L2RDOOR :
case OBJ_L3LDOOR :
case OBJ_L3RDOOR :
UpdateDoor ( oi ) ;
break ;
case OBJ_TORCHL :
case OBJ_TORCHR :
case OBJ_TORCHL2 :
case OBJ_TORCHR2 :
UpdateObjectLight ( oi , 8 ) ;
break ;
case OBJ_SARC :
UpdateSarcoffagus ( oi ) ;
break ;
case OBJ_FLAMEHOLE :
UpdateFlameTrap ( oi ) ;
break ;
case OBJ_TRAPL :
case OBJ_TRAPR :
Obj_Trap ( oi ) ;
break ;
case OBJ_MCIRCLE1 :
case OBJ_MCIRCLE2 :
UpdateCircle ( oi ) ;
break ;
case OBJ_BCROSS :
case OBJ_TBCROSS :
UpdateObjectLight ( oi , 10 ) ;
UpdateBurningCrossDamage ( oi ) ;
break ;
default :
break ;
}
if ( Objects [ oi ] . _oAnimFlag = = 0 )
continue ;
Objects [ oi ] . _oAnimCnt + + ;
if ( Objects [ oi ] . _oAnimCnt < Objects [ oi ] . _oAnimDelay )
continue ;
Objects [ oi ] . _oAnimCnt = 0 ;
Objects [ oi ] . _oAnimFrame + + ;
if ( Objects [ oi ] . _oAnimFrame > Objects [ oi ] . _oAnimLen )
Objects [ oi ] . _oAnimFrame = 1 ;
}
for ( int i = 0 ; i < ActiveObjectCount ; ) {
int oi = ActiveObjects [ i ] ;
if ( Objects [ oi ] . _oDelFlag ) {
DeleteObject ( oi , i ) ;
} else {
i + + ;
}
}
}
void RedoPlayerVision ( )
{
for ( auto & player : Players ) {
if ( player . plractive & & currlevel = = player . plrlevel ) {
ChangeVisionXY ( player . _pvid , player . position . tile ) ;
}
}
}
void MonstCheckDoors ( MonsterStruct & monster )
{
int mx = monster . position . tile . x ;
int my = monster . position . tile . y ;
if ( dObject [ mx - 1 ] [ my - 1 ] ! = 0
| | dObject [ mx ] [ my - 1 ] ! = 0
| | dObject [ mx + 1 ] [ my - 1 ] ! = 0
| | dObject [ mx - 1 ] [ my ] ! = 0
| | dObject [ mx + 1 ] [ my ] ! = 0
| | dObject [ mx - 1 ] [ my + 1 ] ! = 0
| | dObject [ mx ] [ my + 1 ] ! = 0
| | dObject [ mx + 1 ] [ my + 1 ] ! = 0 ) {
for ( int i = 0 ; i < ActiveObjectCount ; i + + ) {
int oi = ActiveObjects [ i ] ;
if ( ( Objects [ oi ] . _otype = = OBJ_L1LDOOR | | Objects [ oi ] . _otype = = OBJ_L1RDOOR ) & & Objects [ oi ] . _oVar4 = = 0 ) {
int dpx = abs ( Objects [ oi ] . position . x - mx ) ;
int dpy = abs ( Objects [ oi ] . position . y - my ) ;
if ( dpx = = 1 & & dpy < = 1 & & Objects [ oi ] . _otype = = OBJ_L1LDOOR )
OperateL1LDoor ( MyPlayerId , oi , true ) ;
if ( dpx < = 1 & & dpy = = 1 & & Objects [ oi ] . _otype = = OBJ_L1RDOOR )
OperateL1RDoor ( MyPlayerId , oi , true ) ;
}
if ( ( Objects [ oi ] . _otype = = OBJ_L2LDOOR | | Objects [ oi ] . _otype = = OBJ_L2RDOOR ) & & Objects [ oi ] . _oVar4 = = 0 ) {
int dpx = abs ( Objects [ oi ] . position . x - mx ) ;
int dpy = abs ( Objects [ oi ] . position . y - my ) ;
if ( dpx = = 1 & & dpy < = 1 & & Objects [ oi ] . _otype = = OBJ_L2LDOOR )
OperateL2LDoor ( MyPlayerId , oi , true ) ;
if ( dpx < = 1 & & dpy = = 1 & & Objects [ oi ] . _otype = = OBJ_L2RDOOR )
OperateL2RDoor ( MyPlayerId , oi , true ) ;
}
if ( ( Objects [ oi ] . _otype = = OBJ_L3LDOOR | | Objects [ oi ] . _otype = = OBJ_L3RDOOR ) & & Objects [ oi ] . _oVar4 = = 0 ) {
int dpx = abs ( Objects [ oi ] . position . x - mx ) ;
int dpy = abs ( Objects [ oi ] . position . y - my ) ;
if ( dpx = = 1 & & dpy < = 1 & & Objects [ oi ] . _otype = = OBJ_L3RDOOR )
OperateL3RDoor ( MyPlayerId , oi , true ) ;
if ( dpx < = 1 & & dpy = = 1 & & Objects [ oi ] . _otype = = OBJ_L3LDOOR )
OperateL3LDoor ( MyPlayerId , oi , true ) ;
}
}
}
}
void ObjChangeMap ( int x1 , int y1 , int x2 , int y2 )
{
for ( int j = y1 ; j < = y2 ; j + + ) {
for ( int i = x1 ; i < = x2 ; i + + ) {
ObjSetMini ( { i , j } , pdungeon [ i ] [ j ] ) ;
dungeon [ i ] [ j ] = pdungeon [ i ] [ j ] ;
}
}
if ( leveltype = = DTYPE_CATHEDRAL & & currlevel < 17 ) {
ObjL1Special ( 2 * x1 + 16 , 2 * y1 + 16 , 2 * x2 + 17 , 2 * y2 + 17 ) ;
AddL1Objs ( 2 * x1 + 16 , 2 * y1 + 16 , 2 * x2 + 17 , 2 * y2 + 17 ) ;
}
if ( leveltype = = DTYPE_CATACOMBS ) {
ObjL2Special ( 2 * x1 + 16 , 2 * y1 + 16 , 2 * x2 + 17 , 2 * y2 + 17 ) ;
AddL2Objs ( 2 * x1 + 16 , 2 * y1 + 16 , 2 * x2 + 17 , 2 * y2 + 17 ) ;
}
}
void ObjChangeMapResync ( int x1 , int y1 , int x2 , int y2 )
{
for ( int j = y1 ; j < = y2 ; j + + ) {
for ( int i = x1 ; i < = x2 ; i + + ) {
ObjSetMini ( { i , j } , pdungeon [ i ] [ j ] ) ;
dungeon [ i ] [ j ] = pdungeon [ i ] [ j ] ;
}
}
if ( leveltype = = DTYPE_CATHEDRAL & & currlevel < 17 ) {
ObjL1Special ( 2 * x1 + 16 , 2 * y1 + 16 , 2 * x2 + 17 , 2 * y2 + 17 ) ;
}
if ( leveltype = = DTYPE_CATACOMBS ) {
ObjL2Special ( 2 * x1 + 16 , 2 * y1 + 16 , 2 * x2 + 17 , 2 * y2 + 17 ) ;
}
}
void TryDisarm ( int pnum , int i )
{
if ( pnum = = MyPlayerId )
NewCursor ( CURSOR_HAND ) ;
if ( ! Objects [ i ] . _oTrapFlag ) {
return ;
}
int trapdisper = 2 * Players [ pnum ] . _pDexterity - 5 * currlevel ;
if ( GenerateRnd ( 100 ) > trapdisper ) {
return ;
}
for ( int j = 0 ; j < ActiveObjectCount ; j + + ) {
bool checkflag = false ;
int oi = ActiveObjects [ j ] ;
int oti = Objects [ oi ] . _otype ;
if ( oti = = OBJ_TRAPL )
checkflag = true ;
if ( oti = = OBJ_TRAPR )
checkflag = true ;
if ( checkflag & & dObject [ Objects [ oi ] . _oVar1 ] [ Objects [ oi ] . _oVar2 ] - 1 = = i ) {
Objects [ oi ] . _oVar4 = 1 ;
Objects [ i ] . _oTrapFlag = false ;
}
}
int oti = Objects [ i ] . _otype ;
if ( oti > = OBJ_TCHEST1 & & oti < = OBJ_TCHEST3 )
Objects [ i ] . _oTrapFlag = false ;
}
int ItemMiscIdIdx ( item_misc_id imiscid )
{
int i = IDI_GOLD ;
while ( AllItemsList [ i ] . iRnd = = IDROP_NEVER | | AllItemsList [ i ] . iMiscId ! = imiscid ) {
i + + ;
}
return i ;
}
bool objectIsDisabled ( int i )
{
if ( ! sgOptions . Gameplay . bDisableCripplingShrines )
return false ;
if ( ( Objects [ i ] . _otype = = OBJ_GOATSHRINE ) | | ( Objects [ i ] . _otype = = OBJ_CAULDRON ) )
return true ;
if ( ( Objects [ i ] . _otype ! = OBJ_SHRINEL ) & & ( Objects [ i ] . _otype ! = OBJ_SHRINER ) )
return false ;
if ( ( Objects [ i ] . _oVar1 = = ShrineFascinating )
| | ( Objects [ i ] . _oVar1 = = ShrineOrnate )
| | ( Objects [ i ] . _oVar1 = = ShrineSacred ) )
return true ;
return false ;
}
void OperateObject ( int pnum , int i , bool teleFlag )
{
bool sendmsg = pnum = = MyPlayerId ;
switch ( Objects [ i ] . _otype ) {
case OBJ_L1LDOOR :
case OBJ_L1RDOOR :
if ( teleFlag ) {
if ( Objects [ i ] . _otype = = OBJ_L1LDOOR )
OperateL1LDoor ( pnum , i , true ) ;
if ( Objects [ i ] . _otype = = OBJ_L1RDOOR )
OperateL1RDoor ( pnum , i , true ) ;
break ;
}
if ( pnum = = MyPlayerId )
OperateL1Door ( pnum , i , true ) ;
break ;
case OBJ_L2LDOOR :
case OBJ_L2RDOOR :
if ( teleFlag ) {
if ( Objects [ i ] . _otype = = OBJ_L2LDOOR )
OperateL2LDoor ( pnum , i , true ) ;
if ( Objects [ i ] . _otype = = OBJ_L2RDOOR )
OperateL2RDoor ( pnum , i , true ) ;
break ;
}
if ( pnum = = MyPlayerId )
OperateL2Door ( pnum , i , true ) ;
break ;
case OBJ_L3LDOOR :
case OBJ_L3RDOOR :
if ( teleFlag ) {
if ( Objects [ i ] . _otype = = OBJ_L3LDOOR )
OperateL3LDoor ( pnum , i , true ) ;
if ( Objects [ i ] . _otype = = OBJ_L3RDOOR )
OperateL3RDoor ( pnum , i , true ) ;
break ;
}
if ( pnum = = MyPlayerId )
OperateL3Door ( pnum , i , true ) ;
break ;
case OBJ_LEVER :
case OBJ_SWITCHSKL :
OperateLever ( pnum , i ) ;
break ;
case OBJ_BOOK2L :
OperateBook ( pnum , i ) ;
break ;
case OBJ_BOOK2R :
OperateSChambBk ( i ) ;
break ;
case OBJ_CHEST1 :
case OBJ_CHEST2 :
case OBJ_CHEST3 :
case OBJ_TCHEST1 :
case OBJ_TCHEST2 :
case OBJ_TCHEST3 :
OperateChest ( pnum , i , sendmsg ) ;
break ;
case OBJ_SARC :
OperateSarc ( pnum , i , sendmsg ) ;
break ;
case OBJ_FLAMELVR :
OperateTrapLvr ( i ) ;
break ;
case OBJ_BLINDBOOK :
case OBJ_BLOODBOOK :
case OBJ_STEELTOME :
OperateBookLever ( pnum , i ) ;
break ;
case OBJ_SHRINEL :
case OBJ_SHRINER :
OperateShrine ( pnum , i , IS_MAGIC ) ;
break ;
case OBJ_SKELBOOK :
case OBJ_BOOKSTAND :
OperateSkelBook ( pnum , i , sendmsg ) ;
break ;
case OBJ_BOOKCASEL :
case OBJ_BOOKCASER :
OperateBookCase ( pnum , i , sendmsg ) ;
break ;
case OBJ_DECAP :
OperateDecap ( pnum , i , sendmsg ) ;
break ;
case OBJ_ARMORSTAND :
case OBJ_WARARMOR :
OperateArmorStand ( pnum , i , sendmsg ) ;
break ;
case OBJ_GOATSHRINE :
OperateGoatShrine ( pnum , i , LS_GSHRINE ) ;
break ;
case OBJ_CAULDRON :
OperateCauldron ( pnum , i , LS_CALDRON ) ;
break ;
case OBJ_BLOODFTN :
case OBJ_PURIFYINGFTN :
case OBJ_MURKYFTN :
case OBJ_TEARFTN :
OperateFountains ( pnum , i ) ;
break ;
case OBJ_STORYBOOK :
OperateStoryBook ( pnum , i ) ;
break ;
case OBJ_PEDISTAL :
OperatePedistal ( pnum , i ) ;
break ;
case OBJ_WARWEAP :
case OBJ_WEAPONRACK :
OperateWeaponRack ( pnum , i , sendmsg ) ;
break ;
case OBJ_MUSHPATCH :
OperateMushPatch ( pnum , i ) ;
break ;
case OBJ_LAZSTAND :
OperateLazStand ( pnum , i ) ;
break ;
case OBJ_SLAINHERO :
OperateSlainHero ( pnum , i ) ;
break ;
case OBJ_SIGNCHEST :
OperateInnSignChest ( pnum , i ) ;
break ;
default :
break ;
}
}
void SyncOpObject ( int pnum , int cmd , int i )
{
switch ( Objects [ i ] . _otype ) {
case OBJ_L1LDOOR :
case OBJ_L1RDOOR :
SyncOpL1Door ( pnum , cmd , i ) ;
break ;
case OBJ_L2LDOOR :
case OBJ_L2RDOOR :
SyncOpL2Door ( pnum , cmd , i ) ;
break ;
case OBJ_L3LDOOR :
case OBJ_L3RDOOR :
SyncOpL3Door ( pnum , cmd , i ) ;
break ;
case OBJ_LEVER :
case OBJ_SWITCHSKL :
OperateLever ( pnum , i ) ;
break ;
case OBJ_CHEST1 :
case OBJ_CHEST2 :
case OBJ_CHEST3 :
case OBJ_TCHEST1 :
case OBJ_TCHEST2 :
case OBJ_TCHEST3 :
OperateChest ( pnum , i , false ) ;
break ;
case OBJ_SARC :
OperateSarc ( pnum , i , false ) ;
break ;
case OBJ_BLINDBOOK :
case OBJ_BLOODBOOK :
case OBJ_STEELTOME :
OperateBookLever ( pnum , i ) ;
break ;
case OBJ_SHRINEL :
case OBJ_SHRINER :
OperateShrine ( pnum , i , IS_MAGIC ) ;
break ;
case OBJ_SKELBOOK :
case OBJ_BOOKSTAND :
OperateSkelBook ( pnum , i , false ) ;
break ;
case OBJ_BOOKCASEL :
case OBJ_BOOKCASER :
OperateBookCase ( pnum , i , false ) ;
break ;
case OBJ_DECAP :
OperateDecap ( pnum , i , false ) ;
break ;
case OBJ_ARMORSTAND :
case OBJ_WARARMOR :
OperateArmorStand ( pnum , i , false ) ;
break ;
case OBJ_GOATSHRINE :
OperateGoatShrine ( pnum , i , LS_GSHRINE ) ;
break ;
case OBJ_CAULDRON :
OperateCauldron ( pnum , i , LS_CALDRON ) ;
break ;
case OBJ_MURKYFTN :
case OBJ_TEARFTN :
OperateFountains ( pnum , i ) ;
break ;
case OBJ_STORYBOOK :
OperateStoryBook ( pnum , i ) ;
break ;
case OBJ_PEDISTAL :
OperatePedistal ( pnum , i ) ;
break ;
case OBJ_WARWEAP :
case OBJ_WEAPONRACK :
OperateWeaponRack ( pnum , i , false ) ;
break ;
case OBJ_MUSHPATCH :
OperateMushPatch ( pnum , i ) ;
break ;
case OBJ_SLAINHERO :
OperateSlainHero ( pnum , i ) ;
break ;
case OBJ_SIGNCHEST :
OperateInnSignChest ( pnum , i ) ;
break ;
default :
break ;
}
}
void BreakObject ( int pnum , int oi )
{
int objdam = 10 ;
if ( pnum ! = - 1 ) {
auto & player = Players [ pnum ] ;
int mind = player . _pIMinDam ;
int maxd = player . _pIMaxDam ;
objdam = GenerateRnd ( maxd - mind + 1 ) + mind ;
objdam + = player . _pDamageMod + player . _pIBonusDamMod + objdam * player . _pIBonusDam / 100 ;
}
switch ( Objects [ oi ] . _otype ) {
case OBJ_CRUX1 :
case OBJ_CRUX2 :
case OBJ_CRUX3 :
BreakCrux ( Objects [ oi ] ) ;
break ;
case OBJ_BARREL :
case OBJ_BARRELEX :
BreakBarrel ( pnum , oi , objdam , false , true ) ;
break ;
default :
break ;
}
}
void SyncBreakObj ( int pnum , int oi )
{
if ( Objects [ oi ] . _otype > = OBJ_BARREL & & Objects [ oi ] . _otype < = OBJ_BARRELEX )
BreakBarrel ( pnum , oi , 0 , true , false ) ;
}
void SyncObjectAnim ( ObjectStruct & object )
{
object_graphic_id index = AllObjects [ object . _otype ] . ofindex ;
const auto & found = std : : find ( std : : begin ( ObjFileList ) , std : : end ( ObjFileList ) , index ) ;
if ( found = = std : : end ( ObjFileList ) ) {
LogCritical ( " Unable to find object_graphic_id {} in list of objects to load, level generation error. " , index ) ;
return ;
}
const int i = std : : distance ( std : : begin ( ObjFileList ) , found ) ;
object . _oAnimData = pObjCels [ i ] . get ( ) ;
switch ( object . _otype ) {
case OBJ_L1LDOOR :
case OBJ_L1RDOOR :
SyncL1Doors ( object ) ;
break ;
case OBJ_L2LDOOR :
case OBJ_L2RDOOR :
SyncL2Doors ( object ) ;
break ;
case OBJ_L3LDOOR :
case OBJ_L3RDOOR :
SyncL3Doors ( object ) ;
break ;
case OBJ_CRUX1 :
case OBJ_CRUX2 :
case OBJ_CRUX3 :
SyncCrux ( object ) ;
break ;
case OBJ_LEVER :
case OBJ_BOOK2L :
case OBJ_SWITCHSKL :
SyncLever ( object ) ;
break ;
case OBJ_BOOK2R :
case OBJ_BLINDBOOK :
case OBJ_STEELTOME :
SyncQSTLever ( object ) ;
break ;
case OBJ_PEDISTAL :
SyncPedestal ( object , { setpc_x , setpc_y } , setpc_w ) ;
break ;
default :
break ;
}
}
void GetObjectStr ( int i )
{
switch ( Objects [ i ] . _otype ) {
case OBJ_CRUX1 :
case OBJ_CRUX2 :
case OBJ_CRUX3 :
strcpy ( infostr , _ ( " Crucified Skeleton " ) ) ;
break ;
case OBJ_LEVER :
case OBJ_FLAMELVR :
strcpy ( infostr , _ ( " Lever " ) ) ;
break ;
case OBJ_L1LDOOR :
case OBJ_L1RDOOR :
case OBJ_L2LDOOR :
case OBJ_L2RDOOR :
case OBJ_L3LDOOR :
case OBJ_L3RDOOR :
if ( Objects [ i ] . _oVar4 = = 1 )
strcpy ( infostr , _ ( " Open Door " ) ) ;
if ( Objects [ i ] . _oVar4 = = 0 )
strcpy ( infostr , _ ( " Closed Door " ) ) ;
if ( Objects [ i ] . _oVar4 = = 2 )
strcpy ( infostr , _ ( " Blocked Door " ) ) ;
break ;
case OBJ_BOOK2L :
if ( setlevel ) {
if ( setlvlnum = = SL_BONECHAMB ) {
strcpy ( infostr , _ ( " Ancient Tome " ) ) ;
} else if ( setlvlnum = = SL_VILEBETRAYER ) {
strcpy ( infostr , _ ( " Book of Vileness " ) ) ;
}
}
break ;
case OBJ_SWITCHSKL :
strcpy ( infostr , _ ( " Skull Lever " ) ) ;
break ;
case OBJ_BOOK2R :
strcpy ( infostr , _ ( " Mythical Book " ) ) ;
break ;
case OBJ_CHEST1 :
case OBJ_TCHEST1 :
strcpy ( infostr , _ ( " Small Chest " ) ) ;
break ;
case OBJ_CHEST2 :
case OBJ_TCHEST2 :
strcpy ( infostr , _ ( " Chest " ) ) ;
break ;
case OBJ_CHEST3 :
case OBJ_TCHEST3 :
case OBJ_SIGNCHEST :
strcpy ( infostr , _ ( " Large Chest " ) ) ;
break ;
case OBJ_SARC :
strcpy ( infostr , _ ( " Sarcophagus " ) ) ;
break ;
case OBJ_BOOKSHELF :
strcpy ( infostr , _ ( " Bookshelf " ) ) ;
break ;
case OBJ_BOOKCASEL :
case OBJ_BOOKCASER :
strcpy ( infostr , _ ( " Bookcase " ) ) ;
break ;
case OBJ_BARREL :
case OBJ_BARRELEX :
if ( currlevel > = 17 & & currlevel < = 20 ) // for hive levels
strcpy ( infostr , _ ( " Pod " ) ) ; //Then a barrel is called a pod
else if ( currlevel > = 21 & & currlevel < = 24 ) // for crypt levels
strcpy ( infostr , _ ( " Urn " ) ) ; //Then a barrel is called an urn
else
strcpy ( infostr , _ ( " Barrel " ) ) ;
break ;
case OBJ_SHRINEL :
case OBJ_SHRINER :
strcpy ( tempstr , fmt : : format ( _ ( /* TRANSLATORS: {:s} will be a name from the Shrine block above */ " {:s} Shrine " ) , _ ( ShrineNames [ Objects [ i ] . _oVar1 ] ) ) . c_str ( ) ) ;
strcpy ( infostr , tempstr ) ;
break ;
case OBJ_SKELBOOK :
strcpy ( infostr , _ ( " Skeleton Tome " ) ) ;
break ;
case OBJ_BOOKSTAND :
strcpy ( infostr , _ ( " Library Book " ) ) ;
break ;
case OBJ_BLOODFTN :
strcpy ( infostr , _ ( " Blood Fountain " ) ) ;
break ;
case OBJ_DECAP :
strcpy ( infostr , _ ( " Decapitated Body " ) ) ;
break ;
case OBJ_BLINDBOOK :
strcpy ( infostr , _ ( " Book of the Blind " ) ) ;
break ;
case OBJ_BLOODBOOK :
strcpy ( infostr , _ ( " Book of Blood " ) ) ;
break ;
case OBJ_PURIFYINGFTN :
strcpy ( infostr , _ ( " Purifying Spring " ) ) ;
break ;
case OBJ_ARMORSTAND :
case OBJ_WARARMOR :
strcpy ( infostr , _ ( " Armor " ) ) ;
break ;
case OBJ_WARWEAP :
strcpy ( infostr , _ ( " Weapon Rack " ) ) ;
break ;
case OBJ_GOATSHRINE :
strcpy ( infostr , _ ( " Goat Shrine " ) ) ;
break ;
case OBJ_CAULDRON :
strcpy ( infostr , _ ( " Cauldron " ) ) ;
break ;
case OBJ_MURKYFTN :
strcpy ( infostr , _ ( " Murky Pool " ) ) ;
break ;
case OBJ_TEARFTN :
strcpy ( infostr , _ ( " Fountain of Tears " ) ) ;
break ;
case OBJ_STEELTOME :
strcpy ( infostr , _ ( " Steel Tome " ) ) ;
break ;
case OBJ_PEDISTAL :
strcpy ( infostr , _ ( " Pedestal of Blood " ) ) ;
break ;
case OBJ_STORYBOOK :
strcpy ( infostr , _ ( StoryBookName [ Objects [ i ] . _oVar3 ] ) ) ;
break ;
case OBJ_WEAPONRACK :
strcpy ( infostr , _ ( " Weapon Rack " ) ) ;
break ;
case OBJ_MUSHPATCH :
strcpy ( infostr , _ ( " Mushroom Patch " ) ) ;
break ;
case OBJ_LAZSTAND :
strcpy ( infostr , _ ( " Vile Stand " ) ) ;
break ;
case OBJ_SLAINHERO :
strcpy ( infostr , _ ( " Slain Hero " ) ) ;
break ;
default :
break ;
}
if ( Players [ MyPlayerId ] . _pClass = = HeroClass : : Rogue ) {
if ( Objects [ i ] . _oTrapFlag ) {
strcpy ( tempstr , fmt : : format ( _ ( /* TRANSLATORS: {:s} will either be a chest or a door */ " Trapped {:s} " ) , infostr ) . c_str ( ) ) ;
strcpy ( infostr , tempstr ) ;
InfoColor = UiFlags : : ColorRed ;
}
}
if ( objectIsDisabled ( i ) ) {
strcpy ( tempstr , fmt : : format ( _ ( /* TRANSLATORS: If user enabled diablo.ini setting "Disable Crippling Shrines" is set to 1; also used for Na-Kruls leaver */ " {:s} (disabled) " ) , infostr ) . c_str ( ) ) ;
strcpy ( infostr , tempstr ) ;
InfoColor = UiFlags : : ColorRed ;
}
}
void OperateNakrulLever ( )
{
if ( currlevel = = 24 ) {
PlaySfxLoc ( IS_CROPEN , { UberRow , UberCol } ) ;
//the part below is the same as SyncNakrulRoom
dPiece [ UberRow ] [ UberCol ] = 298 ;
dPiece [ UberRow ] [ UberCol - 1 ] = 301 ;
dPiece [ UberRow ] [ UberCol - 2 ] = 300 ;
dPiece [ UberRow ] [ UberCol + 1 ] = 299 ;
SetDungeonMicros ( ) ;
}
}
void SyncNakrulRoom ( )
{
dPiece [ UberRow ] [ UberCol ] = 298 ;
dPiece [ UberRow ] [ UberCol - 1 ] = 301 ;
dPiece [ UberRow ] [ UberCol - 2 ] = 300 ;
dPiece [ UberRow ] [ UberCol + 1 ] = 299 ;
SetDungeonMicros ( ) ;
}
void AddNakrulLeaver ( )
{
while ( true ) {
int xp = GenerateRnd ( 80 ) + 16 ;
int yp = GenerateRnd ( 80 ) + 16 ;
if ( RndLocOk ( xp - 1 , yp - 1 )
& & RndLocOk ( xp , yp - 1 )
& & RndLocOk ( xp + 1 , yp - 1 )
& & RndLocOk ( xp - 1 , yp )
& & RndLocOk ( xp , yp )
& & RndLocOk ( xp + 1 , yp )
& & RndLocOk ( xp - 1 , yp + 1 )
& & RndLocOk ( xp , yp + 1 )
& & RndLocOk ( xp + 1 , yp + 1 ) ) {
break ;
}
}
AddObject ( OBJ_LEVER , { UberRow + 3 , UberCol - 1 } ) ;
}
} // namespace devilution