2020-07-15 19:11:23 +00:00
//-------------------------------------------------------------------------
/*
Copyright ( C ) 1996 , 2003 - 3 D Realms Entertainment
Copyright ( C ) 2020 - Christoph Oelckers
This file is part of Duke Nukem 3 D version 1.5 - Atomic Edition
Duke Nukem 3 D is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation ; either version 2
of the License , or ( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .
See the GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
Original Source : 1996 - Todd Replogle
Prepared for public release : 03 / 21 / 2003 - Charlie Wiederhold , 3 D Realms
Modifications for JonoF ' s port by Jonathon Fowler ( jf @ jonof . id . au )
*/
//-------------------------------------------------------------------------
// all code related to startup, up to entering the main loop.
# include "ns.h" // Must come before everything else!
# include "duke3d.h"
# include "m_argv.h"
# include "mapinfo.h"
# include "texturemanager.h"
# include "statusbar.h"
# include "st_start.h"
# include "i_interface.h"
# include "prediction.h"
2020-07-21 20:46:26 +00:00
# include "gamestate.h"
2021-06-01 09:05:26 +00:00
# include "razefont.h"
2022-01-12 22:33:44 +00:00
# include "psky.h"
2022-01-23 23:10:25 +00:00
# include "vm.h"
2022-01-25 23:36:34 +00:00
# include "thingdef.h"
2020-07-15 19:11:23 +00:00
BEGIN_DUKE_NS
void SetDispatcher ( ) ;
void InitCheats ( ) ;
int registerosdcommands ( void ) ;
2021-12-05 13:00:22 +00:00
//---------------------------------------------------------------------------
//
// DObject stuff - everything GC related.
//
//---------------------------------------------------------------------------
2021-12-05 19:55:19 +00:00
IMPLEMENT_CLASS ( DDukeActor , false , true )
IMPLEMENT_POINTERS_START ( DDukeActor )
IMPLEMENT_POINTER ( ownerActor )
IMPLEMENT_POINTER ( hitOwnerActor )
IMPLEMENT_POINTER ( temp_actor )
IMPLEMENT_POINTER ( seek_actor )
IMPLEMENT_POINTERS_END
size_t DDukeActor : : PropagateMark ( )
{
for ( auto & var : uservars )
{
var . Mark ( ) ;
}
2021-12-06 16:00:15 +00:00
return Super : : PropagateMark ( ) ;
2021-12-05 19:55:19 +00:00
}
2021-12-05 13:00:22 +00:00
static void markgcroots ( )
{
GC : : Mark ( camsprite ) ;
GC : : Mark ( BellSprite ) ;
2022-11-28 18:55:28 +00:00
GC : : MarkArray ( spriteq , 1024 ) ;
2021-12-05 13:00:22 +00:00
GC : : Mark ( currentCommentarySprite ) ;
GC : : Mark ( ud . cameraactor ) ;
for ( auto & pl : ps )
{
GC : : Mark ( pl . actor ) ;
GC : : Mark ( pl . actorsqu ) ;
GC : : Mark ( pl . wackedbyactor ) ;
GC : : Mark ( pl . on_crane ) ;
GC : : Mark ( pl . holoduke_on ) ;
GC : : Mark ( pl . somethingonplayer ) ;
GC : : Mark ( pl . access_spritenum ) ;
GC : : Mark ( pl . dummyplayersprite ) ;
GC : : Mark ( pl . newOwner ) ;
for ( auto & var : pl . uservars )
{
var . Mark ( ) ;
}
}
}
2020-07-15 19:11:23 +00:00
//---------------------------------------------------------------------------
//
// game specific command line args go here.
//
//---------------------------------------------------------------------------
static void checkcommandline ( )
{
2020-09-05 14:31:01 +00:00
#if 0
2020-07-15 19:11:23 +00:00
val = Args - > CheckValue ( " -respawn " ) ;
if ( ! val ) val = Args - > CheckValue ( " -t " ) ;
if ( val )
{
if ( * val = = ' 1 ' ) ud . m_respawn_monsters = 1 ;
else if ( * val = = ' 2 ' ) ud . m_respawn_items = 1 ;
else if ( * val = = ' 3 ' ) ud . m_respawn_inventory = 1 ;
else
{
ud . m_respawn_monsters = 1 ;
ud . m_respawn_items = 1 ;
ud . m_respawn_inventory = 1 ;
}
Printf ( " Respawn on. \n " ) ;
}
2020-09-05 14:31:01 +00:00
# endif
2020-07-15 19:11:23 +00:00
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static void genspriteremaps ( void )
{
int j ;
auto fr = fileSystem . OpenFileReader ( " lookup.dat " ) ;
if ( ! fr . isOpen ( ) )
2020-07-20 21:21:27 +00:00
return ;
2020-07-15 19:11:23 +00:00
j = lookups . loadTable ( fr ) ;
if ( j < 0 )
{
if ( j = = - 1 )
Printf ( " ERROR loading \" lookup.dat \" : failed reading enough data. \n " ) ;
return ;
}
uint8_t paldata [ 768 ] ;
2020-07-20 21:21:27 +00:00
for ( j = 1 ; j < = 5 ; j + + )
2020-07-15 19:11:23 +00:00
{
if ( fr . Read ( paldata , 768 ) ! = 768 )
return ;
for ( int k = 0 ; k < 768 ; k + + ) // Build uses 6 bit VGA palettes.
paldata [ k ] = ( paldata [ k ] < < 2 ) | ( paldata [ k ] > > 6 ) ;
2020-07-26 15:55:22 +00:00
paletteSetColorTable ( j , paldata , j = = DREALMSPAL | | j = = ENDINGPAL , j > SLIMEPAL ) ;
2020-07-15 19:11:23 +00:00
}
for ( int i = 0 ; i < 256 ; i + + )
{
// swap red and blue channels.
paldata [ i * 3 ] = GPalette . BaseColors [ i ] . b ;
2020-07-20 21:21:27 +00:00
paldata [ i * 3 + 1 ] = GPalette . BaseColors [ i ] . g ;
paldata [ i * 3 + 2 ] = GPalette . BaseColors [ i ] . r ;
2020-07-15 19:11:23 +00:00
}
paletteSetColorTable ( DRUGPAL , paldata , false , false ) ; // todo: implement this as a shader effect (swap R and B in postprocessing.)
if ( isRR ( ) )
{
uint8_t table [ 256 ] ;
for ( j = 0 ; j < 256 ; j + + )
table [ j ] = j ;
for ( j = 0 ; j < 32 ; j + + )
table [ j ] = j + 32 ;
lookups . makeTable ( 7 , table , 0 , 0 , 0 , 0 ) ;
for ( j = 0 ; j < 256 ; j + + )
table [ j ] = j ;
lookups . makeTable ( 30 , table , 0 , 0 , 0 , 0 ) ;
lookups . makeTable ( 31 , table , 0 , 0 , 0 , 0 ) ;
lookups . makeTable ( 32 , table , 0 , 0 , 0 , 0 ) ;
lookups . makeTable ( 33 , table , 0 , 0 , 0 , 0 ) ;
if ( isRRRA ( ) )
lookups . makeTable ( 105 , table , 0 , 0 , 0 , 0 ) ;
int unk = 63 ;
for ( j = 64 ; j < 80 ; j + + )
{
unk - - ;
table [ j ] = unk ;
table [ j + 16 ] = j - 24 ;
}
table [ 80 ] = 80 ;
table [ 81 ] = 81 ;
for ( j = 0 ; j < 32 ; j + + )
{
table [ j ] = j + 32 ;
}
lookups . makeTable ( 34 , table , 0 , 0 , 0 , 0 ) ;
for ( j = 0 ; j < 256 ; j + + )
table [ j ] = j ;
for ( j = 0 ; j < 16 ; j + + )
table [ j ] = j + 129 ;
for ( j = 16 ; j < 32 ; j + + )
table [ j ] = j + 192 ;
lookups . makeTable ( 35 , table , 0 , 0 , 0 , 0 ) ;
if ( isRRRA ( ) )
{
lookups . makeTable ( 50 , nullptr , 12 * 4 , 12 * 4 , 12 * 4 , 0 ) ;
lookups . makeTable ( 51 , nullptr , 12 * 4 , 12 * 4 , 12 * 4 , 0 ) ;
lookups . makeTable ( 54 , lookups . getTable ( 8 ) , 32 * 4 , 32 * 4 , 32 * 4 , 0 ) ;
}
}
}
//---------------------------------------------------------------------------
//
// Define sky layouts.
// This one's easy - the other games are a total mess
//
//---------------------------------------------------------------------------
static void setupbackdrop ( )
{
2022-01-12 22:33:44 +00:00
static const int16_t moonoff [ 8 ] = { 0 , 2 , 3 , 0 , 2 , 0 , 1 , 0 } ;
static const int16_t orbitoff [ 8 ] = { 0 , 0 , 4 , 0 , 0 , 1 , 2 , 3 } ;
static const int16_t laoff [ 8 ] = { 1 , 2 , 1 , 3 , 4 , 0 , 2 , 3 } ;
static const int16_t defoff [ 8 ] = { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 } ;
static const int16_t defoff1 [ 8 ] = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 0 } ;
static const int16_t defoff4 [ 8 ] = { 4 , 5 , 6 , 7 , 0 , 1 , 2 , 3 } ;
static const int16_t defoff7 [ 8 ] = { 7 , 0 , 1 , 2 , 3 , 4 , 5 , 6 } ;
defineSky ( DEFAULTPSKY , 3 , nullptr ) ;
2022-11-30 13:56:56 +00:00
defineSky ( " CLOUDYOCEAN " , 3 , nullptr ) ;
defineSky ( " MOONSKY12 " , 3 , moonoff ) ;
defineSky ( " BIGORBIT1 " , 3 , orbitoff ) ;
defineSky ( " LA " , 3 , laoff ) ;
2020-07-15 19:11:23 +00:00
if ( isWorldTour ( ) )
{
2022-01-12 22:33:44 +00:00
defineSky ( 5284 , 3 , defoff ) ;
defineSky ( 5412 , 3 , defoff , 80 ) ;
defineSky ( 5420 , 3 , defoff , 80 ) ;
defineSky ( 5450 , 3 , defoff7 , 80 ) ;
defineSky ( 5540 , 3 , defoff , 80 ) ;
defineSky ( 5548 , 3 , defoff , 80 ) ;
defineSky ( 5556 , 3 , defoff1 , 80 ) ;
defineSky ( 5720 , 3 , defoff4 , 80 ) ;
defineSky ( 5814 , 3 , defoff , 80 ) ;
2020-07-15 19:11:23 +00:00
}
2021-05-08 20:08:05 +00:00
if ( isNam ( ) )
{
2022-01-12 22:33:44 +00:00
defineSky ( 212 , 3 , nullptr , 0 , 1 , 140 ) ;
defineSky ( 225 , 3 , nullptr , 0 , 1 , 140 ) ;
2021-05-08 20:08:05 +00:00
}
2022-01-04 15:36:34 +00:00
if ( isWW2GI ( ) & & ( g_gameType & GAMEFLAG_ADDON ) )
{
2022-01-12 22:33:44 +00:00
defineSky ( 1086 , 3 , nullptr , 0 , 1 , 140 ) ;
2022-01-04 15:36:34 +00:00
}
2022-01-13 14:57:25 +00:00
// this sky isn't actually placed wrong - it's just so poorly designed that it needs to be shifted down to hide its shortcomings as good as possible.
if ( isDuke ( ) & & ( g_gameType & GAMEFLAG_DUKEDC ) )
{
defineSky ( 3708 , 3 , nullptr , 0 , 1 , - 40 ) ;
}
2020-07-15 19:11:23 +00:00
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
2020-10-06 17:26:22 +00:00
# define x(a, b) registerName(#a, b);
2022-11-25 16:29:27 +00:00
# define y(a, b) registerName(#a, b);
2020-10-06 17:26:22 +00:00
static void SetTileNames ( )
{
auto registerName = [ ] ( const char * name , int index )
{
2022-01-23 20:30:00 +00:00
TileFiles . addName ( name , index ) ;
2020-10-06 17:26:22 +00:00
} ;
if ( ! isRR ( ) )
{
# include "namelist_d.h"
}
else
{
# include "namelist_r.h"
}
}
# undef x
# undef y
2022-11-25 15:52:08 +00:00
void GameInterface : : LoadGameTextures ( )
{
SetTileNames ( ) ;
}
2022-12-05 19:04:31 +00:00
void GameInterface : : SetupSpecialTextures ( )
{
// set up all special tiles here, before we fully hook up with the texture manager.
tileDelete ( FOF ) ; // portal marker
if ( ! isRR ( ) )
{
tileDelete ( 560 ) ; // the mirror tile.
TileFiles . MakeCanvas ( TILE_VIEWSCR , tileWidth ( 502 ) , tileHeight ( 502 ) ) ;
}
else
{
tileDelete ( 1089 ) ; // the mirror tile.
tileDelete ( 0 ) ; // RR uses this as an empty texture
TileFiles . tileMakeWritable ( 2025 ) ; // bowling lane pin displays
TileFiles . tileMakeWritable ( 2026 ) ;
TileFiles . tileMakeWritable ( 2027 ) ;
TileFiles . tileMakeWritable ( 2028 ) ;
TileFiles . MakeCanvas ( TILE_VIEWSCR , tileWidth ( 1055 ) , tileHeight ( 1055 ) ) ; // not used by the game but all the support code is present, meaning maps are free to use it.
}
TileFiles . lock ( ) ; // from this point on the tile<->texture associations may not change anymore.
}
2020-10-06 17:26:22 +00:00
2021-06-01 09:05:26 +00:00
void GameInterface : : loadPalette ( )
{
paletteLoadFromDisk ( ) ;
genspriteremaps ( ) ;
}
2021-07-20 08:50:46 +00:00
int GameInterface : : GetCurrentSkill ( )
{
return ud . player_skill - 1 ;
}
2020-07-15 19:11:23 +00:00
//---------------------------------------------------------------------------
//
// set up the game module's state
//
//---------------------------------------------------------------------------
2020-08-30 10:49:21 +00:00
void GameInterface : : app_init ( )
2020-07-15 19:11:23 +00:00
{
2021-12-05 13:00:22 +00:00
GC : : AddMarkerFunc ( markgcroots ) ;
2021-11-25 21:33:55 +00:00
2020-08-26 12:33:19 +00:00
if ( isRR ( ) ) C_SetNotifyFontScale ( 0.5 ) ;
2020-07-20 21:21:27 +00:00
ud . god = 0 ;
ud . m_respawn_items = 0 ;
ud . m_respawn_monsters = 0 ;
ud . m_respawn_inventory = 0 ;
ud . cashman = 0 ;
2020-09-05 14:31:01 +00:00
ud . player_skill = 2 ;
2020-07-20 21:21:27 +00:00
ud . wchoice [ 0 ] [ 0 ] = 3 ;
ud . wchoice [ 0 ] [ 1 ] = 4 ;
ud . wchoice [ 0 ] [ 2 ] = 5 ;
ud . wchoice [ 0 ] [ 3 ] = 7 ;
ud . wchoice [ 0 ] [ 4 ] = 8 ;
ud . wchoice [ 0 ] [ 5 ] = 6 ;
ud . wchoice [ 0 ] [ 6 ] = 0 ;
ud . wchoice [ 0 ] [ 7 ] = 2 ;
ud . wchoice [ 0 ] [ 8 ] = 9 ;
ud . wchoice [ 0 ] [ 9 ] = 1 ;
ud . multimode = 1 ;
2020-07-15 19:11:23 +00:00
ud . m_monsters_off = userConfig . nomonsters ;
2020-07-20 21:21:27 +00:00
ps [ 0 ] . aim_mode = 1 ;
2020-11-02 22:53:55 +00:00
ud . cameraactor = nullptr ;
2020-07-15 19:11:23 +00:00
2020-07-19 19:04:22 +00:00
if ( fileSystem . FileExists ( " DUKESW.BIN " ) )
g_gameType | = GAMEFLAG_SHAREWARE ;
2020-07-15 19:11:23 +00:00
2020-07-20 21:21:27 +00:00
numplayers = 1 ;
playerswhenstarted = ud . multimode ;
connectpoint2 [ 0 ] = - 1 ;
2020-07-15 19:11:23 +00:00
2020-07-20 21:21:27 +00:00
SetDispatcher ( ) ;
2022-11-24 20:27:08 +00:00
2020-07-20 21:21:27 +00:00
loadcons ( ) ;
fi . initactorflags ( ) ;
2022-10-21 22:12:17 +00:00
duke_menufont - > Callback ( ) ; // depends on the .CON files so it must be after loadcons
2020-07-15 19:11:23 +00:00
2020-07-20 21:21:27 +00:00
OnEvent ( EVENT_INIT ) ;
2020-07-15 19:11:23 +00:00
2020-07-20 21:21:27 +00:00
//Net_SendClientInfo();
2020-07-15 19:11:23 +00:00
2021-04-03 19:06:02 +00:00
setupbackdrop ( ) ;
2020-07-20 21:21:27 +00:00
SetupGameButtons ( ) ;
InitCheats ( ) ;
checkcommandline ( ) ;
registerosdcommands ( ) ;
screenpeek = myconnectindex ;
2020-10-25 14:06:06 +00:00
C_InitConback ( TexMan . CheckForTexture ( " MENUSCREEN " , ETextureType : : Any ) , false , 0.75 ) ;
2020-07-20 21:21:27 +00:00
if ( ud . multimode > 1 )
{
ud . m_monsters_off = 1 ;
2020-09-05 14:31:01 +00:00
//ud.player_skill = 0;
2020-07-20 21:21:27 +00:00
}
ud . last_level = - 1 ;
2020-08-23 15:47:05 +00:00
enginecompatibility_mode = ENGINECOMPATIBILITY_19961112 ; //bVanilla;
2020-09-26 14:18:44 +00:00
S_ParseDeveloperCommentary ( ) ;
2020-07-15 19:11:23 +00:00
}
2020-07-20 21:21:27 +00:00
2022-01-25 23:36:34 +00:00
2022-02-20 23:19:26 +00:00
void CallInitialize ( DDukeActor * actor )
{
2022-01-16 23:51:40 +00:00
IFVIRTUALPTR ( actor , DDukeActor , Initialize )
2022-02-20 23:19:26 +00:00
{
2022-01-19 17:52:52 +00:00
VMValue val [ 2 ] = { actor } ;
VMCall ( func , val , 1 , nullptr , 0 ) ;
2022-02-20 23:19:26 +00:00
}
}
void CallTick ( DDukeActor * actor )
{
IFVIRTUALPTR ( actor , DDukeActor , Tick )
{
VMValue val = actor ;
VMCall ( func , & val , 1 , nullptr , 0 ) ;
}
}
void CallAction ( DDukeActor * actor )
{
IFVIRTUALPTR ( actor , DDukeActor , RunState )
{
VMValue val = actor ;
VMCall ( func , & val , 1 , nullptr , 0 ) ;
}
}
2022-01-17 23:30:43 +00:00
void CallOnHit ( DDukeActor * actor , DDukeActor * hitter )
{
IFVIRTUALPTR ( actor , DDukeActor , onHit )
{
VMValue val [ 2 ] = { actor , hitter } ;
VMCall ( func , val , 2 , nullptr , 0 ) ;
}
}
2022-11-15 09:17:23 +00:00
void CallOnHurt ( DDukeActor * actor , player_struct * hitter )
{
IFVIRTUALPTR ( actor , DDukeActor , onHurt )
{
VMValue val [ 2 ] = { actor , hitter } ;
VMCall ( func , val , 2 , nullptr , 0 ) ;
}
}
2022-11-21 20:33:27 +00:00
void CallOnTouch ( DDukeActor * actor , player_struct * hitter )
{
IFVIRTUALPTR ( actor , DDukeActor , onTouch )
{
VMValue val [ 2 ] = { actor , hitter } ;
VMCall ( func , val , 2 , nullptr , 0 ) ;
}
}
2022-11-15 09:17:23 +00:00
2022-01-21 00:04:08 +00:00
bool CallOnUse ( DDukeActor * actor , player_struct * user )
2022-11-14 09:22:22 +00:00
{
2022-01-21 00:04:08 +00:00
int nval = false ;
2022-11-14 09:22:22 +00:00
IFVIRTUALPTR ( actor , DDukeActor , onUse )
{
VMValue val [ 2 ] = { actor , user } ;
2022-01-21 00:04:08 +00:00
VMReturn ret ( & nval ) ;
VMCall ( func , val , 2 , & ret , 1 ) ;
2022-11-14 09:22:22 +00:00
}
2022-01-21 00:04:08 +00:00
return nval ;
2022-11-14 09:22:22 +00:00
}
2022-01-17 23:30:43 +00:00
2022-11-22 16:53:46 +00:00
void CallOnMotoSmash ( DDukeActor * actor , player_struct * hitter )
{
IFVIRTUALPTR ( actor , DDukeActor , onMotoSmash )
{
VMValue val [ 2 ] = { actor , hitter } ;
VMCall ( func , val , 2 , nullptr , 0 ) ;
}
}
2022-11-20 10:58:19 +00:00
void CallOnRespawn ( DDukeActor * actor , int low )
{
IFVIRTUALPTR ( actor , DDukeActor , onRespawn )
{
VMValue val [ 2 ] = { actor , low } ;
VMCall ( func , val , 2 , nullptr , 0 ) ;
}
}
2022-01-19 17:52:52 +00:00
bool CallAnimate ( DDukeActor * actor , tspritetype * tspr )
{
int nval = false ;
IFVIRTUALPTR ( actor , DDukeActor , animate )
{
VMReturn ret ( & nval ) ;
2022-01-21 00:04:08 +00:00
VMValue val [ 3 ] = { actor , tspr } ;
VMCall ( func , val , 3 , & ret , 1 ) ;
2022-01-19 17:52:52 +00:00
}
return nval ;
}
2022-11-20 12:57:51 +00:00
void CallStaticSetup ( DDukeActor * actor )
{
IFVIRTUALPTR ( actor , DDukeActor , StaticSetup )
{
VMValue val = actor ;
VMCall ( func , & val , 1 , nullptr , 0 ) ;
}
}
2022-01-19 17:52:52 +00:00
2022-11-29 12:06:42 +00:00
bool CallShootThis ( DDukeActor * clsdef , DDukeActor * actor , int pn , const DVector3 & spos , DAngle sang )
{
int rv = 0 ;
VMReturn ret ( & rv ) ;
IFVIRTUALPTR ( clsdef , DDukeActor , ShootThis )
{
VMValue val [ ] = { clsdef , actor , pn > = 0 ? & ps [ pn ] : nullptr , spos . X , spos . Y , spos . Z , sang . Degrees ( ) } ;
VMCall ( func , val , 7 , & ret , 1 ) ;
}
return rv ;
}
2022-11-30 18:39:06 +00:00
void CallPlayFTASound ( DDukeActor * actor )
{
IFVIRTUALPTR ( actor , DDukeActor , PlayFTASound )
{
VMValue val = actor ;
VMCall ( func , & val , 1 , nullptr , 0 ) ;
}
}
2022-12-02 22:26:55 +00:00
void CallStandingOn ( DDukeActor * actor , player_struct * p )
{
IFVIRTUALPTR ( actor , DDukeActor , StandingOn )
{
VMValue val [ ] = { actor , p } ;
VMCall ( func , val , 2 , nullptr , 0 ) ;
}
}
2022-11-30 18:39:06 +00:00
2022-12-04 08:18:57 +00:00
CCMD ( changewalltexture )
{
if ( argv . argc ( ) < 2 ) return ;
int tile = TileFiles . tileForName ( argv [ 1 ] ) ;
if ( tile < 0 ) tile = ( int ) strtol ( argv [ 1 ] , nullptr , 10 ) ;
HitInfoBase hit ;
hitscan ( ps [ 0 ] . actor - > spr . pos , ps [ 0 ] . cursector , DVector3 ( ps [ 0 ] . actor - > spr . Angles . Yaw . ToVector ( ) , 0 ) * 1024 , hit , CLIPMASK1 ) ;
if ( hit . hitWall )
{
2022-12-05 19:42:12 +00:00
hit . hitWall - > wallpicnum = tile ;
2022-12-04 08:18:57 +00:00
}
}
2020-07-15 19:11:23 +00:00
END_DUKE_NS