Merge branch 'master' into lua-additions

This commit is contained in:
Alam Ed Arias 2017-01-13 16:47:57 -05:00
commit 94025ae947
63 changed files with 6348 additions and 5638 deletions

3002
SRB2.cbp

File diff suppressed because it is too large Load diff

3
debian/docs vendored
View file

@ -1,2 +1 @@
readme.txt
readme.txt
README.md

View file

@ -179,6 +179,9 @@ endif
ifdef LINUX
UNIXCOMMON=1
ifndef NOGME
HAVE_LIBGME=1
endif
endif
ifdef SOLARIS
@ -315,6 +318,13 @@ LIBS+=$(PNG_LDFLAGS)
CFLAGS+=$(PNG_CFLAGS)
endif
ZLIB_PKGCONFIG?=zlib
ZLIB_CFLAGS?=$(shell $(PKG_CONFIG) $(ZLIB_PKGCONFIG) --cflags)
ZLIB_LDFLAGS?=$(shell $(PKG_CONFIG) $(ZLIB_PKGCONFIG) --libs)
LIBS+=$(ZLIB_LDFLAGS)
CFLAGS+=$(ZLIB_CFLAGS)
ifdef HAVE_LIBGME
OPTS+=-DHAVE_LIBGME
@ -366,6 +376,14 @@ endif
OPTS:=-fno-exceptions $(OPTS)
ifdef MOBJCONSISTANCY
OPTS+=-DMOBJCONSISTANCY
endif
ifdef PACKETDROP
OPTS+=-DPACKETDROP
endif
ifdef DEBUGMODE
# build with debugging information
@ -375,7 +393,7 @@ ifdef GCC48
else
CFLAGS+=-O0
endif
CFLAGS+= -Wall -DPARANOIA -DRANGECHECK
CFLAGS+= -Wall -DPARANOIA -DRANGECHECK -DPACKETDROP -DMOBJCONSISTANCY
else

File diff suppressed because it is too large Load diff

View file

@ -59,7 +59,7 @@ typedef enum
// Add non-PT_CANFAIL packet types here to avoid breaking MS compatibility.
PT_CANFAIL, // This is kind of a priority. Anything bigger than CANFAIL
// allows HSendPacket(,true,,) to return false.
// allows HSendPacket(*, true, *, *) to return false.
// In addition, this packet can't occupy all the available slots.
PT_FILEFRAGMENT = PT_CANFAIL, // A part of a file.
@ -76,11 +76,16 @@ typedef enum
NUMPACKETTYPE
} packettype_t;
#ifdef PACKETDROP
void Command_Drop(void);
void Command_Droprate(void);
#endif
#if defined(_MSC_VER)
#pragma pack(1)
#endif
// client to server packet
// Client to server packet
typedef struct
{
UINT8 client_tic;
@ -89,7 +94,7 @@ typedef struct
ticcmd_t cmd;
} ATTRPACK clientcmd_pak;
// splitscreen packet
// Splitscreen packet
// WARNING: must have the same format of clientcmd_pak, for more easy use
typedef struct
{
@ -110,16 +115,16 @@ typedef struct
UINT8 starttic;
UINT8 numtics;
UINT8 numslots; // "Slots filled": Highest player number in use plus one.
ticcmd_t cmds[45]; // normally [BACKUPTIC][MAXPLAYERS] but too large
ticcmd_t cmds[45]; // Normally [BACKUPTIC][MAXPLAYERS] but too large
} ATTRPACK servertics_pak;
// sent to client when all consistency data
// Sent to client when all consistency data
// for players has been restored
typedef struct
{
UINT32 randomseed;
//ctf flag stuff
// CTF flag stuff
SINT8 flagplayer[2];
INT32 flagloose[2];
INT32 flagflags[2];
@ -127,11 +132,11 @@ typedef struct
fixed_t flagy[2];
fixed_t flagz[2];
UINT32 ingame; // spectator bit for each player
UINT32 ctfteam; // if not spectator, then which team?
UINT32 ingame; // Spectator bit for each player
INT32 ctfteam[MAXPLAYERS]; // Which team? (can't be 1 bit, since in regular Match there are no teams)
// Resynch game scores and the like all at once
UINT32 score[MAXPLAYERS]; // Everyone's score.
UINT32 score[MAXPLAYERS]; // Everyone's score
INT16 numboxes[MAXPLAYERS];
INT16 totalring[MAXPLAYERS];
tic_t realtime[MAXPLAYERS];
@ -140,14 +145,14 @@ typedef struct
typedef struct
{
//player stuff
// Player stuff
UINT8 playernum;
// Do not send anything visual related.
// Only send data that we need to know for physics.
UINT8 playerstate; //playerstate_t
UINT32 pflags; //pflags_t
UINT8 panim; //panim_t
UINT8 playerstate; // playerstate_t
UINT32 pflags; // pflags_t
UINT8 panim; // panim_t
angle_t aiming;
INT32 currentweapon;
@ -176,9 +181,9 @@ typedef struct
UINT8 charability;
UINT8 charability2;
UINT32 charflags;
UINT32 thokitem; //mobjtype_t
UINT32 spinitem; //mobjtype_t
UINT32 revitem; //mobjtype_t
UINT32 thokitem; // mobjtype_t
UINT32 spinitem; // mobjtype_t
UINT32 revitem; // mobjtype_t
fixed_t actionspd;
fixed_t mindash;
fixed_t maxdash;
@ -234,7 +239,7 @@ typedef struct
INT32 onconveyor;
//player->mo stuff
UINT8 hasmo; //boolean
UINT8 hasmo; // Boolean
INT32 health;
angle_t angle;
@ -262,10 +267,10 @@ typedef struct
typedef struct
{
UINT8 version; // different versions don't work
UINT8 subversion; // contains build version
UINT8 version; // Different versions don't work
UINT8 subversion; // Contains build version
// server launch stuffs
// Server launch stuffs
UINT8 serverplayer;
UINT8 totalslotnum; // "Slots": highest player number in use plus one.
@ -279,18 +284,18 @@ typedef struct
UINT8 gametype;
UINT8 modifiedgame;
SINT8 adminplayer; // needs to be signed
SINT8 adminplayer; // Needs to be signed
char server_context[8]; // unique context id, generated at server startup.
char server_context[8]; // Unique context id, generated at server startup.
UINT8 varlengthinputs[0]; // playernames and netvars
UINT8 varlengthinputs[0]; // Playernames and netvars
} ATTRPACK serverconfig_pak;
typedef struct {
UINT8 fileid;
UINT32 position;
UINT16 size;
UINT8 data[0]; // size is variable using hardware_MAXPACKETLENGTH
UINT8 data[0]; // Size is variable using hardware_MAXPACKETLENGTH
} ATTRPACK filetx_pak;
#ifdef _MSC_VER
@ -299,14 +304,14 @@ typedef struct {
typedef struct
{
UINT8 version; // different versions don't work
UINT8 subversion; // contains build version
UINT8 version; // Different versions don't work
UINT8 subversion; // Contains build version
UINT8 localplayers;
UINT8 mode;
} ATTRPACK clientconfig_pak;
#define MAXSERVERNAME 32
// this packet is too large
// This packet is too large
typedef struct
{
UINT8 version;
@ -372,45 +377,45 @@ typedef struct
} ATTRPACK plrconfig;
//
// Network packet data.
// Network packet data
//
typedef struct
{
UINT32 checksum;
UINT8 ack; // if not null the node asks for acknowledgement, the receiver must resend the ack
UINT8 ackreturn; // the return of the ack number
UINT8 ack; // If not zero the node asks for acknowledgement, the receiver must resend the ack
UINT8 ackreturn; // The return of the ack number
UINT8 packettype;
UINT8 reserved; // padding
UINT8 reserved; // Padding
union
{
clientcmd_pak clientpak; // 144 bytes
client2cmd_pak client2pak; // 200 bytes
servertics_pak serverpak; // 132495 bytes
serverconfig_pak servercfg; // 773 bytes
resynchend_pak resynchend; //
resynch_pak resynchpak; //
UINT8 resynchgot; //
UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes
filetx_pak filetxpak; // 139 bytes
clientconfig_pak clientcfg; // 136 bytes
serverinfo_pak serverinfo; // 1024 bytes
serverrefuse_pak serverrefuse; // 65025 bytes
askinfo_pak askinfo; // 61 bytes
msaskinfo_pak msaskinfo; // 22 bytes
plrinfo playerinfo[MAXPLAYERS]; // 1152 bytes
plrconfig playerconfig[MAXPLAYERS]; // (up to) 896 bytes
clientcmd_pak clientpak; // 144 bytes
client2cmd_pak client2pak; // 200 bytes
servertics_pak serverpak; // 132495 bytes (more around 360, no?)
serverconfig_pak servercfg; // 773 bytes
resynchend_pak resynchend; //
resynch_pak resynchpak; //
UINT8 resynchgot; //
UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes (wut??? 64k??? More like 257 bytes...)
filetx_pak filetxpak; // 139 bytes
clientconfig_pak clientcfg; // 136 bytes
serverinfo_pak serverinfo; // 1024 bytes
serverrefuse_pak serverrefuse; // 65025 bytes (somehow I feel like those values are garbage...)
askinfo_pak askinfo; // 61 bytes
msaskinfo_pak msaskinfo; // 22 bytes
plrinfo playerinfo[MAXPLAYERS]; // 1152 bytes (I'd say 36~38)
plrconfig playerconfig[MAXPLAYERS]; // (up to) 896 bytes (welp they ARE)
#ifdef NEWPING
UINT32 pingtable[MAXPLAYERS]; // 128 bytes
UINT32 pingtable[MAXPLAYERS]; // 128 bytes
#endif
} u; // this is needed to pack diff packet types data together
} u; // This is needed to pack diff packet types data together
} ATTRPACK doomdata_t;
#if defined(_MSC_VER)
#pragma pack()
#endif
#define MAXSERVERLIST 64 // depends only on the display
#define MAXSERVERLIST 64 // Depends only on the display
typedef struct
{
SINT8 node;
@ -421,7 +426,7 @@ extern serverelem_t serverlist[MAXSERVERLIST];
extern UINT32 serverlistcount;
extern INT32 mapchangepending;
// points inside doomcom
// Points inside doomcom
extern doomdata_t *netbuffer;
extern consvar_t cv_playbackspeed;
@ -442,7 +447,7 @@ extern consvar_t cv_playbackspeed;
#define KICK_MSG_CUSTOM_BAN 8
extern boolean server;
extern boolean dedicated; // for dedicated server
extern boolean dedicated; // For dedicated server
extern UINT16 software_MAXPACKETLENGTH;
extern boolean acceptnewnode;
extern SINT8 servernode;
@ -457,11 +462,11 @@ extern UINT32 playerpingtable[MAXPLAYERS];
extern consvar_t cv_joinnextround, cv_allownewplayer, cv_maxplayers, cv_resynchattempts, cv_blamecfail, cv_maxsend;
// used in d_net, the only dependence
// Used in d_net, the only dependence
tic_t ExpandTics(INT32 low);
void D_ClientServerInit(void);
// initialise the other field
// Initialise the other field
void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum));
void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam);
void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam); // splitsreen player
@ -479,14 +484,14 @@ void CL_RemoveSplitscreenPlayer(void);
void CL_Reset(void);
void CL_ClearPlayer(INT32 playernum);
void CL_UpdateServerList(boolean internetsearch, INT32 room);
// is there a game running
// Is there a game running
boolean Playing(void);
// Broadcasts special packets to other players
// to notify of game exit
void D_QuitNetGame(void);
//? how many ticks to run?
//? How many ticks to run?
void TryRunTics(tic_t realtic);
// extra data for lmps

View file

@ -31,15 +31,15 @@
//
// NETWORKING
//
// gametic is the tic about to be (or currently being) run
// server:
// gametic is the tic about to (or currently being) run
// Server:
// maketic is the tic that hasn't had control made for it yet
// nettics: is the tic for each node
// firsttictosend: is the lowest value of nettics
// client:
// neededtic: is the tic needed by the client to run the game
// firsttictosend: is used to optimize a condition
// normally maketic >= gametic > 0
// nettics is the tic for each node
// firstticstosend is the lowest value of nettics
// Client:
// neededtic is the tic needed by the client to run the game
// firstticstosend is used to optimize a condition
// Normally maketic >= gametic > 0
#define FORCECLOSE 0x8000
tic_t connectiontimeout = (15*TICRATE);
@ -129,9 +129,9 @@ boolean Net_GetNetStat(void)
// -----------------------------------------------------------------
// Some structs and functions for acknowledgement of packets
// -----------------------------------------------------------------
#define MAXACKPACKETS 96 // minimum number of nodes
#define MAXACKPACKETS 96 // Minimum number of nodes (wat)
#define MAXACKTOSEND 96
#define URGENTFREESLOTENUM 10
#define URGENTFREESLOTNUM 10
#define ACKTOSENDTIMEOUT (TICRATE/11)
#ifndef NONET
@ -139,10 +139,10 @@ typedef struct
{
UINT8 acknum;
UINT8 nextacknum;
UINT8 destinationnode;
tic_t senttime;
UINT16 length;
UINT16 resentnum;
UINT8 destinationnode; // The node to send the ack to
tic_t senttime; // The time when the ack was sent
UINT16 length; // The packet size
UINT16 resentnum; // The number of
union {
SINT8 raw[MAXPACKETLENGTH];
doomdata_t data;
@ -212,11 +212,16 @@ FUNCMATH static INT32 cmpack(UINT8 a, UINT8 b)
return d;
}
// return a free acknum and copy netbuffer in the ackpak table
/** Sets freeack to a free acknum and copies the netbuffer in the ackpak table
*
* \param freeack The address to store the free acknum at
* \param lowtimer ???
* \return True if a free acknum was found
*/
static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
{
node_t *node = &nodes[doomcom->remotenode];
INT32 i, numfreeslote = 0;
INT32 i, numfreeslot = 0;
if (cmpack((UINT8)((node->remotefirstack + MAXACKTOSEND) % 256), node->nextacknum) < 0)
{
@ -227,10 +232,13 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
for (i = 0; i < MAXACKPACKETS; i++)
if (!ackpak[i].acknum)
{
// for low priority packet, make sure let freeslotes so urgents packets can be sent
numfreeslote++;
if (netbuffer->packettype >= PT_CANFAIL && numfreeslote < URGENTFREESLOTENUM)
continue;
// For low priority packets, make sure to let freeslots so urgent packets can be sent
if (netbuffer->packettype >= PT_CANFAIL)
{
numfreeslot++;
if (numfreeslot <= URGENTFREESLOTNUM)
continue;
}
ackpak[i].acknum = node->nextacknum;
ackpak[i].nextacknum = node->nextacknum;
@ -241,7 +249,7 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
ackpak[i].length = doomcom->datalength;
if (lowtimer)
{
// lowtime mean can't be sent now so try it soon as possible
// Lowtime means can't be sent now so try it as soon as possible
ackpak[i].senttime = 0;
ackpak[i].resentnum = 1;
}
@ -254,7 +262,7 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
*freeack = ackpak[i].acknum;
sendackpacket++; // for stat
sendackpacket++; // For stat
return true;
}
@ -266,14 +274,14 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
return false;
}
// Get a ack to send in the queu of this node
// Get a ack to send in the queue of this node
static UINT8 GetAcktosend(INT32 node)
{
nodes[node].lasttimeacktosend_sent = I_GetTime();
return nodes[node].firstacktosend;
}
static void Removeack(INT32 i)
static void RemoveAck(INT32 i)
{
INT32 node = ackpak[i].destinationnode;
#ifndef NEWPING
@ -294,27 +302,27 @@ static void Removeack(INT32 i)
Net_CloseConnection(node);
}
// we have got a packet proceed the ack request and ack return
// We have got a packet, proceed the ack request and ack return
static boolean Processackpak(void)
{
INT32 i;
boolean goodpacket = true;
node_t *node = &nodes[doomcom->remotenode];
// received an ack return, so remove the ack in the list
// Received an ack return, so remove the ack in the list
if (netbuffer->ackreturn && cmpack(node->remotefirstack, netbuffer->ackreturn) < 0)
{
node->remotefirstack = netbuffer->ackreturn;
// search the ackbuffer and free it
// Search the ackbuffer and free it
for (i = 0; i < MAXACKPACKETS; i++)
if (ackpak[i].acknum && ackpak[i].destinationnode == node - nodes
&& cmpack(ackpak[i].acknum, netbuffer->ackreturn) <= 0)
{
Removeack(i);
RemoveAck(i);
}
}
// received a packet with ack, queue it to send the ack back
// Received a packet with ack, queue it to send the ack back
if (netbuffer->ack)
{
UINT8 ack = netbuffer->ack;
@ -323,23 +331,23 @@ static boolean Processackpak(void)
{
DEBFILE(va("Discard(1) ack %d (duplicated)\n", ack));
duppacket++;
goodpacket = false; // discard packet (duplicate)
goodpacket = false; // Discard packet (duplicate)
}
else
{
// check if it is not already in the queue
// Check if it is not already in the queue
for (i = node->acktosend_tail; i != node->acktosend_head; i = (i+1) % MAXACKTOSEND)
if (node->acktosend[i] == ack)
{
DEBFILE(va("Discard(2) ack %d (duplicated)\n", ack));
duppacket++;
goodpacket = false; // discard packet (duplicate)
goodpacket = false; // Discard packet (duplicate)
break;
}
if (goodpacket)
{
// is a good packet so increment the acknowledge number,
// then search for a "hole" in the queue
// Is a good packet so increment the acknowledge number,
// Then search for a "hole" in the queue
UINT8 nextfirstack = (UINT8)(node->firstacktosend + 1);
if (!nextfirstack)
nextfirstack = 1;
@ -383,10 +391,10 @@ static boolean Processackpak(void)
}
}
}
else // out of order packet
else // Out of order packet
{
// don't increment firsacktosend, put it in asktosend queue
// will be incremented when the nextfirstack comes (code above)
// Don't increment firsacktosend, put it in asktosend queue
// Will be incremented when the nextfirstack comes (code above)
UINT8 newhead = (UINT8)((node->acktosend_head+1) % MAXACKTOSEND);
DEBFILE(va("out of order packet (%d expected)\n", nextfirstack));
if (newhead != node->acktosend_tail)
@ -394,8 +402,8 @@ static boolean Processackpak(void)
node->acktosend[node->acktosend_head] = ack;
node->acktosend_head = newhead;
}
else // buffer full discard packet, sender will resend it
{ // we can admit the packet but we will not detect the duplication after :(
else // Buffer full discard packet, sender will resend it
{ // We can admit the packet but we will not detect the duplication after :(
DEBFILE("no more freeackret\n");
goodpacket = false;
}
@ -430,25 +438,24 @@ static void GotAcks(void)
if (ackpak[i].acknum && ackpak[i].destinationnode == doomcom->remotenode)
{
if (ackpak[i].acknum == netbuffer->u.textcmd[j])
Removeack(i);
else
// nextacknum is first equal to acknum, then when receiving bigger ack
// there is big chance the packet is lost
// when resent, nextacknum = nodes[node].nextacknum
// will redo the same but with different value
if (cmpack(ackpak[i].nextacknum, netbuffer->u.textcmd[j]) <= 0
&& ackpak[i].senttime > 0)
{
ackpak[i].senttime--; // hurry up
}
RemoveAck(i);
// nextacknum is first equal to acknum, then when receiving bigger ack
// there is big chance the packet is lost
// When resent, nextacknum = nodes[node].nextacknum
// will redo the same but with different value
else if (cmpack(ackpak[i].nextacknum, netbuffer->u.textcmd[j]) <= 0
&& ackpak[i].senttime > 0)
{
ackpak[i].senttime--; // hurry up
}
}
}
#endif
static inline void Net_ConnectionTimeout(INT32 node)
{
// send a very special packet to self (hack the reboundstore queue)
// main code will handle it
// Send a very special packet to self (hack the reboundstore queue)
// Main code will handle it
reboundstore[rebound_head].packettype = PT_NODETIMEOUT;
reboundstore[rebound_head].ack = 0;
reboundstore[rebound_head].ackreturn = 0;
@ -456,12 +463,12 @@ static inline void Net_ConnectionTimeout(INT32 node)
reboundsize[rebound_head] = (INT16)(BASEPACKETSIZE + 1);
rebound_head = (rebound_head+1) % MAXREBOUND;
// do not redo it quickly (if we do not close connection it is
// Do not redo it quickly (if we do not close connection it is
// for a good reason!)
nodes[node].lasttimepacketreceived = I_GetTime();
}
// resend the data if needed
// Resend the data if needed
void Net_AckTicker(void)
{
#ifndef NONET
@ -497,7 +504,7 @@ void Net_AckTicker(void)
ackpak[i].senttime = I_GetTime();
ackpak[i].resentnum++;
ackpak[i].nextacknum = node->nextacknum;
retransmit++; // for stat
retransmit++; // For stat
HSendPacket((INT32)(node - nodes), false, ackpak[i].acknum,
(size_t)(ackpak[i].length - BASEPACKETSIZE));
}
@ -505,11 +512,11 @@ void Net_AckTicker(void)
for (i = 1; i < MAXNETNODES; i++)
{
// this is something like node open flag
// This is something like node open flag
if (nodes[i].firstacktosend)
{
// we haven't sent a packet for a long time
// acknowledge packet if needed
// We haven't sent a packet for a long time
// Acknowledge packet if needed
if (nodes[i].lasttimeacktosend_sent + ACKTOSENDTIMEOUT < I_GetTime())
Net_SendAcks(i);
@ -523,9 +530,9 @@ void Net_AckTicker(void)
#endif
}
// remove last packet received ack before resending the ackret
// Remove last packet received ack before resending the ackreturn
// (the higher layer doesn't have room, or something else ....)
void Net_UnAcknowledgPacket(INT32 node)
void Net_UnAcknowledgePacket(INT32 node)
{
#ifdef NONET
(void)node;
@ -564,20 +571,29 @@ void Net_UnAcknowledgPacket(INT32 node)
#endif
}
boolean Net_AllAckReceived(void)
{
#ifndef NONET
/** Checks if all acks have been received
*
* \return True if all acks have been received
*
*/
static boolean Net_AllAcksReceived(void)
{
INT32 i;
for (i = 0; i < MAXACKPACKETS; i++)
if (ackpak[i].acknum)
return false;
#endif
return true;
}
#endif
// wait for all ackreturns with timeout in seconds
/** Waits for all ackreturns
*
* \param timeout Timeout in seconds
*
*/
void Net_WaitAllAckReceived(UINT32 timeout)
{
#ifdef NONET
@ -587,7 +603,7 @@ void Net_WaitAllAckReceived(UINT32 timeout)
timeout = tictac + timeout*NEWTICRATE;
HGetPacket();
while (timeout > I_GetTime() && !Net_AllAckReceived())
while (timeout > I_GetTime() && !Net_AllAcksReceived())
{
while (tictac == I_GetTime())
I_Sleep();
@ -598,18 +614,18 @@ void Net_WaitAllAckReceived(UINT32 timeout)
#endif
}
static void InitNode(INT32 node)
static void InitNode(node_t *node)
{
nodes[node].acktosend_head = nodes[node].acktosend_tail = 0;
node->acktosend_head = node->acktosend_tail = 0;
#ifndef NEWPING
nodes[node].ping = PINGDEFAULT;
nodes[node].varping = VARPINGDEFAULT;
nodes[node].timeout = TIMEOUT(nodes[node].ping,nodes[node].varping);
node->ping = PINGDEFAULT;
node->varping = VARPINGDEFAULT;
node->timeout = TIMEOUT(node->ping, node->varping);
#endif
nodes[node].firstacktosend = 0;
nodes[node].nextacknum = 1;
nodes[node].remotefirstack = 0;
nodes[node].flags = 0;
node->firstacktosend = 0;
node->nextacknum = 1;
node->remotefirstack = 0;
node->flags = 0;
}
static void InitAck(void)
@ -622,9 +638,14 @@ static void InitAck(void)
#endif
for (i = 0; i < MAXNETNODES; i++)
InitNode(i);
InitNode(&nodes[i]);
}
/** Removes all acks of a given packet type
*
* \param packettype The packet type to forget
*
*/
void Net_AbortPacketType(UINT8 packettype)
{
#ifdef NONET
@ -676,8 +697,8 @@ void Net_CloseConnection(INT32 node)
ackpak[i].acknum = 0;
}
InitNode(node);
AbortSendFiles(node);
InitNode(&nodes[node]);
SV_AbortSendFiles(node);
I_NetFreeNodenum(node);
#endif
}
@ -729,9 +750,15 @@ static void fprintfstring(char *s, size_t len)
}
if (mode)
fprintf(debugfile, "]");
}
static void fprintfstringnewline(char *s, size_t len)
{
fprintfstring(s, len);
fprintf(debugfile, "\n");
}
/// \warning Keep this up-to-date if you add/remove/rename packet types
static const char *packettypename[NUMPACKETTYPE] =
{
"NOTHING",
@ -749,15 +776,22 @@ static const char *packettypename[NUMPACKETTYPE] =
"ASKINFO",
"SERVERINFO",
"PLAYERINFO",
"REQUESTFILE",
"ASKINFOVIAMS",
"PLAYERCONFIGS",
"RESYNCHEND",
"RESYNCHGET",
"FILEFRAGMENT",
"TEXTCMD",
"TEXTCMD2",
"CLIENTJOIN",
"NODETIMEOUT",
"RESYNCHING",
#ifdef NEWPING
"PING"
#endif
};
static void DebugPrintpacket(const char *header)
@ -770,20 +804,29 @@ static void DebugPrintpacket(const char *header)
{
case PT_ASKINFO:
case PT_ASKINFOVIAMS:
fprintf(debugfile, " time %u\n", (tic_t)LONG(netbuffer->u.askinfo.time) );
fprintf(debugfile, " time %u\n", (tic_t)LONG(netbuffer->u.askinfo.time));
break;
case PT_CLIENTJOIN:
fprintf(debugfile, " number %d mode %d\n", netbuffer->u.clientcfg.localplayers,
netbuffer->u.clientcfg.mode);
break;
case PT_SERVERTICS:
{
servertics_pak *serverpak = &netbuffer->u.serverpak;
ticcmd_t *cmd = &serverpak->cmds[serverpak->numslots * serverpak->numtics];
size_t ntxtcmd = &((UINT8 *)netbuffer)[doomcom->datalength] - (UINT8 *)cmd;
fprintf(debugfile, " firsttic %u ply %d tics %d ntxtcmd %s\n ",
(UINT32)ExpandTics(netbuffer->u.serverpak.starttic), netbuffer->u.serverpak.numslots,
netbuffer->u.serverpak.numtics,
sizeu1((size_t)(&((UINT8 *)netbuffer)[doomcom->datalength] - (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots*netbuffer->u.serverpak.numtics])));
fprintfstring((char *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots*netbuffer->u.serverpak.numtics],(size_t)(
&((UINT8 *)netbuffer)[doomcom->datalength] - (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots*netbuffer->u.serverpak.numtics]));
(UINT32)ExpandTics(serverpak->starttic), serverpak->numslots, serverpak->numtics, sizeu1(ntxtcmd));
fprintfstring((char *)cmd, 3);
if (ntxtcmd > 4)
{
fprintf(debugfile, "[%s]", netxcmdnames[*(((UINT8 *)cmd) + 3) - 1]);
fprintfstring(((char *)cmd) + 4, ntxtcmd - 4);
}
fprintf(debugfile, "\n");
break;
}
case PT_CLIENTCMD:
case PT_CLIENT2CMD:
case PT_CLIENTMIS:
@ -797,7 +840,8 @@ static void DebugPrintpacket(const char *header)
case PT_TEXTCMD:
case PT_TEXTCMD2:
fprintf(debugfile, " length %d\n ", netbuffer->u.textcmd[0]);
fprintfstring((char *)netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]);
fprintf(debugfile, "[%s]", netxcmdnames[netbuffer->u.textcmd[1] - 1]);
fprintfstringnewline((char *)netbuffer->u.textcmd + 2, netbuffer->u.textcmd[0] - 1);
break;
case PT_SERVERCFG:
fprintf(debugfile, " playerslots %d clientnode %d serverplayer %d "
@ -813,7 +857,7 @@ static void DebugPrintpacket(const char *header)
netbuffer->u.serverinfo.maxplayer, netbuffer->u.serverinfo.mapname,
netbuffer->u.serverinfo.fileneedednum,
(UINT32)LONG(netbuffer->u.serverinfo.time));
fprintfstring((char *)netbuffer->u.serverinfo.fileneeded,
fprintfstringnewline((char *)netbuffer->u.serverinfo.fileneeded,
(UINT8)((UINT8 *)netbuffer + doomcom->datalength
- (UINT8 *)netbuffer->u.serverinfo.fileneeded));
break;
@ -827,20 +871,100 @@ static void DebugPrintpacket(const char *header)
break;
case PT_REQUESTFILE:
default: // write as a raw packet
fprintfstring((char *)netbuffer->u.textcmd,
fprintfstringnewline((char *)netbuffer->u.textcmd,
(UINT8)((UINT8 *)netbuffer + doomcom->datalength - (UINT8 *)netbuffer->u.textcmd));
break;
}
}
#endif
#ifdef PACKETDROP
static INT32 packetdropquantity[NUMPACKETTYPE] = {0};
static INT32 packetdroprate = 0;
void Command_Drop(void)
{
INT32 packetquantity;
const char *packetname;
size_t i;
if (COM_Argc() < 2)
{
CONS_Printf("drop <packettype> [quantity]: drop packets\n"
"drop reset: cancel all packet drops");
return;
}
if (!(stricmp(COM_Argv(1), "reset") && stricmp(COM_Argv(1), "cancel") && stricmp(COM_Argv(1), "stop")))
{
memset(packetdropquantity, 0, sizeof(packetdropquantity));
return;
}
if (COM_Argc() >= 3)
{
packetquantity = atoi(COM_Argv(2));
if (packetquantity <= 0 && COM_Argv(2)[0] != '0')
{
CONS_Printf("Invalid quantity\n");
return;
}
}
else
packetquantity = -1;
packetname = COM_Argv(1);
if (!(stricmp(packetname, "all") && stricmp(packetname, "any")))
for (i = 0; i < NUMPACKETTYPE; i++)
packetdropquantity[i] = packetquantity;
else
{
for (i = 0; i < NUMPACKETTYPE; i++)
if (!stricmp(packetname, packettypename[i]))
{
packetdropquantity[i] = packetquantity;
return;
}
CONS_Printf("Unknown packet name\n");
}
}
void Command_Droprate(void)
{
INT32 droprate;
if (COM_Argc() < 2)
{
CONS_Printf("Packet drop rate: %d%%\n", packetdroprate);
return;
}
droprate = atoi(COM_Argv(1));
if ((droprate <= 0 && COM_Argv(1)[0] != '0') || droprate > 100)
{
CONS_Printf("Packet drop rate must be between 0 and 100!\n");
return;
}
packetdroprate = droprate;
}
static boolean ShouldDropPacket(void)
{
return (packetdropquantity[netbuffer->packettype])
|| (packetdroprate != 0 && rand() < (RAND_MAX * (packetdroprate / 100.f))) || packetdroprate == 100;
}
#endif
//
// HSendPacket
//
boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlength)
{
doomcom->datalength = (INT16)(packetlength + BASEPACKETSIZE);
if (node == 0) // packet is to go back to us
if (node == 0) // Packet is to go back to us
{
if ((rebound_head+1) % MAXREBOUND == rebound_tail)
{
@ -871,7 +995,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
(void)reliable;
(void)acknum;
#else
// do this before GetFreeAcknum because this function backup
// do this before GetFreeAcknum because this function backups
// the current packet
doomcom->remotenode = (INT16)node;
if (doomcom->datalength <= 0)
@ -884,7 +1008,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
return false;
}
if (node < MAXNETNODES) // can be a broadcast
if (node < MAXNETNODES) // Can be a broadcast
netbuffer->ackreturn = GetAcktosend(node);
else
netbuffer->ackreturn = 0;
@ -905,20 +1029,30 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
netbuffer->ack = acknum;
netbuffer->checksum = NetbufferChecksum();
sendbytes += packetheaderlength + doomcom->datalength; // for stat
sendbytes += packetheaderlength + doomcom->datalength; // For stat
// simulate internet :)
if (true || rand()<(INT32)RAND_MAX/5)
#ifdef PACKETDROP
// Simulate internet :)
//if (rand() >= (INT32)(RAND_MAX * (PACKETLOSSRATE / 100.f)))
if (!ShouldDropPacket())
{
#endif
#ifdef DEBUGFILE
if (debugfile)
DebugPrintpacket("SEND");
DebugPrintpacket("SENT");
#endif
I_NetSend();
#ifdef PACKETDROP
}
else
{
if (packetdropquantity[netbuffer->packettype] > 0)
packetdropquantity[netbuffer->packettype]--;
#ifdef DEBUGFILE
else if (debugfile)
DebugPrintpacket("NOTSEND");
if (debugfile)
DebugPrintpacket("NOT SENT");
#endif
}
#endif
#endif // ndef NONET
@ -933,7 +1067,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
//
boolean HGetPacket(void)
{
// get a packet from self
// Get a packet from self
if (rebound_tail != rebound_head)
{
M_Memcpy(netbuffer, &reboundstore[rebound_tail], reboundsize[rebound_tail]);
@ -963,11 +1097,11 @@ boolean HGetPacket(void)
if (doomcom->remotenode == -1)
return false;
getbytes += packetheaderlength + doomcom->datalength; // for stat
getbytes += packetheaderlength + doomcom->datalength; // For stat
if (doomcom->remotenode >= MAXNETNODES)
{
DEBFILE(va("receive packet from node %d !\n", doomcom->remotenode));
DEBFILE(va("Received packet from node %d!\n", doomcom->remotenode));
continue;
}
@ -1230,4 +1364,6 @@ void D_CloseConnection(void)
netgame = false;
addedtogame = false;
}
D_ResetTiccmds();
}

View file

@ -18,10 +18,10 @@
#ifndef __D_NET__
#define __D_NET__
// Max computers in a game.
// Max computers in a game
#define MAXNETNODES 32
#define BROADCASTADDR MAXNETNODES
#define MAXSPLITSCREENPLAYERS 2 // max number of players on a single computer
#define MAXSPLITSCREENPLAYERS 2 // Max number of players on a single computer
#define STATLENGTH (TICRATE*2)
@ -32,17 +32,16 @@ extern float lostpercent, duppercent, gamelostpercent;
extern INT32 packetheaderlength;
boolean Net_GetNetStat(void);
extern INT32 getbytes;
extern INT64 sendbytes; // realtime updated
extern INT64 sendbytes; // Realtime updated
extern SINT8 nodetoplayer[MAXNETNODES];
extern SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen)
extern UINT8 playerpernode[MAXNETNODES]; // used specialy for scplitscreen
extern boolean nodeingame[MAXNETNODES]; // set false as nodes leave game
extern SINT8 nodetoplayer2[MAXNETNODES]; // Say the numplayer for this node if any (splitscreen)
extern UINT8 playerpernode[MAXNETNODES]; // Used specially for splitscreen
extern boolean nodeingame[MAXNETNODES]; // Set false as nodes leave game
void Net_AckTicker(void);
boolean Net_AllAckReceived(void);
// if reliable return true if packet sent, 0 else
// If reliable return true if packet sent, 0 else
boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum,
size_t packetlength);
boolean HGetPacket(void);
@ -52,9 +51,10 @@ void D_SaveBan(void);
#endif
boolean D_CheckNetGame(void);
void D_CloseConnection(void);
void Net_UnAcknowledgPacket(INT32 node);
void Net_UnAcknowledgePacket(INT32 node);
void Net_CloseConnection(INT32 node);
void Net_AbortPacketType(UINT8 packettype);
void Net_SendAcks(INT32 node);
void Net_WaitAllAckReceived(UINT32 timeout);
#endif

View file

@ -365,6 +365,35 @@ boolean splitscreen = false;
boolean circuitmap = false;
INT32 adminplayer = -1;
/// \warning Keep this up-to-date if you add/remove/rename net text commands
const char *netxcmdnames[MAXNETXCMD - 1] =
{
"NAMEANDCOLOR",
"WEAPONPREF",
"KICK",
"NETVAR",
"SAY",
"MAP",
"EXITLEVEL",
"ADDFILE",
"PAUSE",
"ADDPLAYER",
"TEAMCHANGE",
"CLEARSCORES",
"LOGIN",
"VERIFIED",
"RANDOMSEED",
"RUNSOC",
"REQADDFILE",
"DELFILE",
"SETMOTD",
"SUICIDE",
#ifdef HAVE_BLUA
"LUACMD",
"LUAVAR"
#endif
};
// =========================================================================
// SERVER STARTUP
// =========================================================================
@ -3960,7 +3989,7 @@ static void Command_Archivetest_f(void)
}
// assign mobjnum
i = 0;
i = 1;
for (th = thinkercap.next; th != &thinkercap; th = th->next)
if (th->function.acp1 == (actionf_p1)P_MobjThinker)
((mobj_t *)th)->mobjnum = i++;
@ -4055,8 +4084,7 @@ static void Skin_OnChange(void)
if (!Playing())
return; // do whatever you want
if (!(cv_debug || devparm) && !(multiplayer || netgame) // In single player.
&& (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_CONTINUING))
if (!(cv_debug || devparm) && !(multiplayer || netgame)) // In single player.
{
CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name);
return;

View file

@ -101,7 +101,6 @@ extern consvar_t cv_recycler;
extern consvar_t cv_itemfinder;
extern consvar_t cv_inttime, cv_advancemap, cv_playersforexit;
extern consvar_t cv_soniccd;
extern consvar_t cv_match_scoring;
extern consvar_t cv_overtime;
extern consvar_t cv_startinglives;
@ -162,6 +161,8 @@ typedef enum
MAXNETXCMD
} netxcmd_t;
extern const char *netxcmdnames[MAXNETXCMD - 1];
#if defined(_MSC_VER)
#pragma pack(1)
#endif

View file

@ -62,34 +62,37 @@
#include <errno.h>
static void SendFile(INT32 node, const char *filename, UINT8 fileid);
static void SV_SendFile(INT32 node, const char *filename, UINT8 fileid);
// sender structure
// Sender structure
typedef struct filetx_s
{
INT32 ram;
char *filename; // name of the file or ptr of the data in ram
UINT32 size;
union {
char *filename; // Name of the file
char *ram; // Pointer to the data in RAM
} id;
UINT32 size; // Size of the file
UINT8 fileid;
INT32 node; // destination
struct filetx_s *next; // a queue
INT32 node; // Destination
struct filetx_s *next; // Next file in the list
} filetx_t;
// current transfers (one for each node)
// Current transfers (one for each node)
typedef struct filetran_s
{
filetx_t *txlist;
UINT32 position;
FILE *currentfile;
filetx_t *txlist; // Linked list of all files for the node
UINT32 position; // The current position in the file
FILE *currentfile; // The file currently being sent/received
} filetran_t;
static filetran_t transfer[MAXNETNODES];
// read time of file: stat _stmtime
// write time of file: utime
// Read time of file: stat _stmtime
// Write time of file: utime
// receiver structure
INT32 fileneedednum;
fileneeded_t fileneeded[MAX_WADFILES];
// Receiver structure
INT32 fileneedednum; // Number of files needed to join the server
fileneeded_t fileneeded[MAX_WADFILES]; // List of needed files
char downloaddir[256] = "DOWNLOAD";
#ifdef CLIENT_LOADINGSCREEN
@ -100,6 +103,7 @@ INT32 lastfilenum = 0;
/** Fills a serverinfo packet with information about wad files loaded.
*
* \todo Give this function a better name since it is in global scope.
*
*/
UINT8 *PutFileNeeded(void)
{
@ -111,19 +115,19 @@ UINT8 *PutFileNeeded(void)
for (i = 0; i < numwadfiles; i++)
{
// if it has only music/sound lumps, mark it as unimportant
// If it has only music/sound lumps, mark it as unimportant
if (W_VerifyNMUSlumps(wadfiles[i]->filename))
filestatus = 0;
else
filestatus = 1; // important
filestatus = 1; // Important
// Store in the upper four bits
if (!cv_downloading.value)
filestatus += (2 << 4); // won't send
filestatus += (2 << 4); // Won't send
else if ((wadfiles[i]->filesize > (UINT32)cv_maxsend.value * 1024))
filestatus += (0 << 4); // won't send
filestatus += (0 << 4); // Won't send
else
filestatus += (1 << 4); // will send if requested
filestatus += (1 << 4); // Will send if requested
bytesused += (nameonlylength(wadfilename) + 22);
@ -144,7 +148,12 @@ UINT8 *PutFileNeeded(void)
return p;
}
// parse the serverinfo packet and fill fileneeded table on client
/** Parses the serverinfo packet and fills the fileneeded table on client
*
* \param fileneedednum_parm The number of files needed to join the server
* \param fileneededstr The memory block containing the list of needed files
*
*/
void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr)
{
INT32 i;
@ -155,14 +164,14 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr)
p = (UINT8 *)fileneededstr;
for (i = 0; i < fileneedednum; i++)
{
fileneeded[i].status = FS_NOTFOUND;
filestatus = READUINT8(p);
fileneeded[i].status = FS_NOTFOUND; // We haven't even started looking for the file yet
filestatus = READUINT8(p); // The first byte is the file status
fileneeded[i].important = (UINT8)(filestatus & 3);
fileneeded[i].willsend = (UINT8)(filestatus >> 4);
fileneeded[i].totalsize = READUINT32(p);
fileneeded[i].phandle = NULL;
READSTRINGN(p, fileneeded[i].filename, MAX_WADPATH);
READMEM(p, fileneeded[i].md5sum, 16);
fileneeded[i].totalsize = READUINT32(p); // The four next bytes are the file size
fileneeded[i].file = NULL; // The file isn't open yet
READSTRINGN(p, fileneeded[i].filename, MAX_WADPATH); // The next bytes are the file name
READMEM(p, fileneeded[i].md5sum, 16); // The last 16 bytes are the file checksum
}
}
@ -171,13 +180,16 @@ void CL_PrepareDownloadSaveGame(const char *tmpsave)
fileneedednum = 1;
fileneeded[0].status = FS_REQUESTED;
fileneeded[0].totalsize = UINT32_MAX;
fileneeded[0].phandle = NULL;
fileneeded[0].file = NULL;
memset(fileneeded[0].md5sum, 0, 16);
strcpy(fileneeded[0].filename, tmpsave);
}
/** Checks the server to see if we CAN download all the files,
* before starting to create them and requesting.
*
* \return True if we can download all the files
*
*/
boolean CL_CheckDownloadable(void)
{
@ -239,8 +251,12 @@ boolean CL_CheckDownloadable(void)
return false;
}
/** Send requests for files in the ::fileneeded table with a status of
/** Sends requests for files in the ::fileneeded table with a status of
* ::FS_NOTFOUND.
*
* \return True if the packet was successfully sent
* \note Sends a PT_REQUESTFILE packet
*
*/
boolean CL_SendRequestFile(void)
{
@ -298,11 +314,17 @@ void Got_RequestFilePak(INT32 node)
if (id == 0xFF)
break;
READSTRINGN(p, wad, MAX_WADPATH);
SendFile(node, wad, id);
SV_SendFile(node, wad, id);
}
}
// client check if the fileneeded aren't already loaded or on the disk
/** Checks if the files needed aren't already loaded or on the disk
*
* \return 0 if some files are missing
* 1 if all files exist
* 2 if some already loaded files are not requested or are in a different order
*
*/
INT32 CL_CheckFiles(void)
{
INT32 i, j;
@ -333,7 +355,7 @@ INT32 CL_CheckFiles(void)
}
if (j < numwadfiles && W_VerifyNMUSlumps(wadfiles[j]->filename))
{
// unimportant on our side. still don't care.
// Unimportant on our side. still don't care.
++j;
continue;
}
@ -343,11 +365,11 @@ INT32 CL_CheckFiles(void)
if (i >= fileneedednum || j >= numwadfiles)
return 2;
// for the sake of speed, only bother with a md5 check
// For the sake of speed, only bother with a md5 check
if (memcmp(wadfiles[j]->md5sum, fileneeded[i].md5sum, 16))
return 2;
// it's accounted for! let's keep going.
// It's accounted for! let's keep going.
CONS_Debug(DBG_NETPLAY, "'%s' accounted for\n", fileneeded[i].filename);
fileneeded[i].status = FS_OPEN;
++i;
@ -360,7 +382,7 @@ INT32 CL_CheckFiles(void)
{
CONS_Debug(DBG_NETPLAY, "searching for '%s' ", fileneeded[i].filename);
// check in allready loaded files
// Check in already loaded files
for (j = 1; wadfiles[j]; j++)
{
nameonly(strcpy(wadfilename, wadfiles[j]->filename));
@ -383,7 +405,7 @@ INT32 CL_CheckFiles(void)
return ret;
}
// load it now
// Load it now
void CL_LoadServerFiles(void)
{
INT32 i;
@ -394,7 +416,7 @@ void CL_LoadServerFiles(void)
for (i = 1; i < fileneedednum; i++)
{
if (fileneeded[i].status == FS_OPEN)
continue; // already loaded
continue; // Already loaded
else if (fileneeded[i].status == FS_FOUND)
{
P_AddWadFile(fileneeded[i].filename, NULL);
@ -423,133 +445,200 @@ void CL_LoadServerFiles(void)
DEBFILE(va("File %s found but with different md5sum\n", fileneeded[i].filename));
}
else if (fileneeded[i].important)
I_Error("Try to load file %s with status of %d\n", fileneeded[i].filename,
fileneeded[i].status);
{
const char *s;
switch(fileneeded[i].status)
{
case FS_NOTFOUND:
s = "FS_NOTFOUND";
break;
case FS_REQUESTED:
s = "FS_REQUESTED";
break;
case FS_DOWNLOADING:
s = "FS_DOWNLOADING";
break;
default:
s = "unknown";
break;
}
I_Error("Try to load file \"%s\" with status of %d (%s)\n", fileneeded[i].filename,
fileneeded[i].status, s);
}
}
}
// little optimization to test if there is a file in the queue
static INT32 filetosend = 0;
// Number of files to send
// Little optimization to quickly test if there is a file in the queue
static INT32 filestosend = 0;
static void SendFile(INT32 node, const char *filename, UINT8 fileid)
/** Adds a file to the file list for a node
*
* \param node The node to send the file to
* \param filename The file to send
* \param fileid ???
* \sa SV_SendRam
*
*/
static void SV_SendFile(INT32 node, const char *filename, UINT8 fileid)
{
filetx_t **q;
filetx_t *p;
filetx_t **q; // A pointer to the "next" field of the last file in the list
filetx_t *p; // The new file request
INT32 i;
char wadfilename[MAX_WADPATH];
// Find the last file in the list and set a pointer to its "next" field
q = &transfer[node].txlist;
while (*q)
q = &((*q)->next);
// Allocate a file request and append it to the file list
p = *q = (filetx_t *)malloc(sizeof (filetx_t));
if (p)
memset(p, 0, sizeof (filetx_t));
else
I_Error("SendFile: No more ram\n");
p->filename = (char *)malloc(MAX_WADPATH);
if (!p->filename)
I_Error("SendFile: No more ram\n");
if (!p)
I_Error("SV_SendFile: No more memory\n");
// a minimum of security, can get only file in srb2 direcory
strlcpy(p->filename, filename, MAX_WADPATH);
nameonly(p->filename);
// Initialise with zeros
memset(p, 0, sizeof (filetx_t));
// check first in wads loaded the majority of case
// Allocate the file name
p->id.filename = (char *)malloc(MAX_WADPATH);
if (!p->id.filename)
I_Error("SV_SendFile: No more memory\n");
// Set the file name and get rid of the path
strlcpy(p->id.filename, filename, MAX_WADPATH);
nameonly(p->id.filename);
// Look for the requested file through all loaded files
for (i = 0; wadfiles[i]; i++)
{
strlcpy(wadfilename, wadfiles[i]->filename, MAX_WADPATH);
nameonly(wadfilename);
if (!stricmp(wadfilename, p->filename))
if (!stricmp(wadfilename, p->id.filename))
{
// copy filename with full path
strlcpy(p->filename, wadfiles[i]->filename, MAX_WADPATH);
// Copy file name with full path
strlcpy(p->id.filename, wadfiles[i]->filename, MAX_WADPATH);
break;
}
}
// Handle non-loaded file requests
if (!wadfiles[i])
{
DEBFILE(va("%s not found in wadfiles\n", filename));
// this formerly checked if (!findfile(p->filename, NULL, true))
// This formerly checked if (!findfile(p->id.filename, NULL, true))
// not found
// don't inform client (probably hacker)
// Not found
// Don't inform client (probably someone who thought they could leak 2.2 ACZ)
DEBFILE(va("Client %d request %s: not found\n", node, filename));
free(p->filename);
free(p->id.filename);
free(p);
*q = NULL;
return;
}
// Handle huge file requests (i.e. bigger than cv_maxsend.value KB)
if (wadfiles[i]->filesize > (UINT32)cv_maxsend.value * 1024)
{
// too big
// don't inform client (client sucks, man)
// Too big
// Don't inform client (client sucks, man)
DEBFILE(va("Client %d request %s: file too big, not sending\n", node, filename));
free(p->filename);
free(p->id.filename);
free(p);
*q = NULL;
return;
}
DEBFILE(va("Sending file %s (id=%d) to %d\n", filename, fileid, node));
p->ram = SF_FILE;
p->ram = SF_FILE; // It's a file, we need to close it and free its name once we're done sending it
p->fileid = fileid;
p->next = NULL; // end of list
filetosend++;
p->next = NULL; // End of list
filestosend++;
}
void SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid)
/** Adds a memory block to the file list for a node
*
* \param node The node to send the memory block to
* \param data The memory block to send
* \param size The size of the block in bytes
* \param freemethod How to free the block after it has been sent
* \param fileid ???
* \sa SV_SendFile
*
*/
void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid)
{
filetx_t **q;
filetx_t *p;
filetx_t **q; // A pointer to the "next" field of the last file in the list
filetx_t *p; // The new file request
// Find the last file in the list and set a pointer to its "next" field
q = &transfer[node].txlist;
while (*q)
q = &((*q)->next);
// Allocate a file request and append it to the file list
p = *q = (filetx_t *)malloc(sizeof (filetx_t));
if (p)
memset(p, 0, sizeof (filetx_t));
else
I_Error("SendRam: No more ram\n");
p->ram = freemethod;
p->filename = data;
if (!p)
I_Error("SV_SendRam: No more memory\n");
// Initialise with zeros
memset(p, 0, sizeof (filetx_t));
p->ram = freemethod; // Remember how to free the memory block for when we're done sending it
p->id.ram = data;
p->size = (UINT32)size;
p->fileid = fileid;
p->next = NULL; // end of list
p->next = NULL; // End of list
DEBFILE(va("Sending ram %p(size:%u) to %d (id=%u)\n",p->filename,p->size,node,fileid));
DEBFILE(va("Sending ram %p(size:%u) to %d (id=%u)\n",p->id.ram,p->size,node,fileid));
filetosend++;
filestosend++;
}
static void EndSend(INT32 node)
/** Stops sending a file for a node, and removes the file request from the list,
* either because the file has been fully sent or because the node was disconnected
*
* \param node The destination
*
*/
static void SV_EndFileSend(INT32 node)
{
filetx_t *p = transfer[node].txlist;
// Free the file request according to the freemethod parameter used with SV_SendFile/Ram
switch (p->ram)
{
case SF_FILE:
case SF_FILE: // It's a file, close it and free its filename
if (transfer[node].currentfile)
fclose(transfer[node].currentfile);
free(p->filename);
free(p->id.filename);
break;
case SF_Z_RAM:
Z_Free(p->filename);
case SF_Z_RAM: // It's a memory block allocated with Z_Alloc or the likes, use Z_Free
Z_Free(p->id.ram);
break;
case SF_RAM:
free(p->filename);
case SF_NOFREERAM:
case SF_RAM: // It's a memory block allocated with malloc, use free
free(p->id.ram);
case SF_NOFREERAM: // Nothing to free
break;
}
// Remove the file request from the list
transfer[node].txlist = p->next;
transfer[node].currentfile = NULL;
free(p);
filetosend--;
// Indicate that the transmission is over
transfer[node].currentfile = NULL;
filestosend--;
}
#define PACKETPERTIC net_bandwidth/(TICRATE*software_MAXPACKETLENGTH)
void FiletxTicker(void)
/** Handles file transmission
*
*
*/
void SV_FileSendTicker(void)
{
static INT32 currentnode = 0;
filetx_pak *p;
@ -557,12 +646,12 @@ void FiletxTicker(void)
filetx_t *f;
INT32 packetsent = PACKETPERTIC, ram, i;
if (!filetosend)
if (!filestosend)
return;
if (!packetsent)
packetsent++;
// (((sendbytes-nowsentbyte)*TICRATE)/(I_GetTime()-starttime)<(UINT32)net_bandwidth)
while (packetsent-- && filetosend != 0)
while (packetsent-- && filestosend != 0)
{
for (i = currentnode, ram = 0; ram < MAXNETNODES;
i = (i+1) % MAXNETNODES, ram++)
@ -571,24 +660,25 @@ void FiletxTicker(void)
goto found;
}
// no transfer to do
I_Error("filetosend=%d but no filetosend found\n", filetosend);
I_Error("filestosend=%d but no file to send found\n", filestosend);
found:
currentnode = (i+1) % MAXNETNODES;
f = transfer[i].txlist;
ram = f->ram;
if (!transfer[i].currentfile) // file not already open
// Open the file if it isn't open yet, or
if (!transfer[i].currentfile)
{
if (!ram)
if (!ram) // Sending a file
{
long filesize;
transfer[i].currentfile =
fopen(f->filename, "rb");
fopen(f->id.filename, "rb");
if (!transfer[i].currentfile)
I_Error("File %s does not exist",
f->filename);
f->id.filename);
fseek(transfer[i].currentfile, 0, SEEK_END);
filesize = ftell(transfer[i].currentfile);
@ -596,45 +686,48 @@ void FiletxTicker(void)
// Nobody wants to transfer a file bigger
// than 4GB!
if (filesize >= LONG_MAX)
I_Error("filesize of %s is too large", f->filename);
if (-1 == filesize)
I_Error("Error getting filesize of %s", f->filename);
I_Error("filesize of %s is too large", f->id.filename);
if (filesize == -1)
I_Error("Error getting filesize of %s", f->id.filename);
f->size = (UINT32)filesize;
fseek(transfer[i].currentfile, 0, SEEK_SET);
}
else
transfer[i].currentfile = (FILE *)1;
else // Sending RAM
transfer[i].currentfile = (FILE *)1; // Set currentfile to a non-null value to indicate that it is open
transfer[i].position = 0;
}
// Build a packet containing a file fragment
p = &netbuffer->u.filetxpak;
size = software_MAXPACKETLENGTH - (FILETXHEADER + BASEPACKETSIZE);
if (f->size-transfer[i].position < size)
size = f->size-transfer[i].position;
if (ram)
M_Memcpy(p->data, &f->filename[transfer[i].position], size);
M_Memcpy(p->data, &f->id.ram[transfer[i].position], size);
else if (fread(p->data, 1, size, transfer[i].currentfile) != size)
I_Error("FiletxTicker: can't read %s byte on %s at %d because %s", sizeu1(size), f->filename, transfer[i].position, strerror(ferror(transfer[i].currentfile)));
I_Error("SV_FileSendTicker: can't read %s byte on %s at %d because %s", sizeu1(size), f->id.filename, transfer[i].position, strerror(ferror(transfer[i].currentfile)));
p->position = LONG(transfer[i].position);
// put flag so receiver know the totalsize
// Put flag so receiver knows the total size
if (transfer[i].position + size == f->size)
p->position |= LONG(0x80000000);
p->fileid = f->fileid;
p->size = SHORT((UINT16)size);
netbuffer->packettype = PT_FILEFRAGMENT;
if (!HSendPacket(i, true, 0, FILETXHEADER + size)) // reliable SEND
{ // not sent for some odd reason, retry at next call
if (!ram)
fseek(transfer[i].currentfile,transfer[i].position,SEEK_SET);
// exit the while (can't send this one so why should i send the next?)
break;
// Send the packet
if (HSendPacket(i, true, 0, FILETXHEADER + size)) // Reliable SEND
{ // Success
transfer[i].position = (UINT32)(transfer[i].position + size);
if (transfer[i].position == f->size) // Finish?
SV_EndFileSend(i);
}
else // success
{
transfer[i].position = (UINT32)(size+transfer[i].position);
if (transfer[i].position == f->size) // finish ?
EndSend(i);
else
{ // Not sent for some odd reason, retry at next call
if (!ram)
fseek(transfer[i].currentfile,transfer[i].position, SEEK_SET);
// Exit the while (can't send this one so why should i send the next?)
break;
}
}
}
@ -646,16 +739,18 @@ void Got_Filetxpak(void)
if (filenum >= fileneedednum)
{
DEBFILE(va("fileframent not needed %d>%d\n",filenum, fileneedednum));
DEBFILE(va("fileframent not needed %d>%d\n", filenum, fileneedednum));
return;
}
if (fileneeded[filenum].status == FS_REQUESTED)
{
if (fileneeded[filenum].phandle) I_Error("Got_Filetxpak: allready open file\n");
fileneeded[filenum].phandle = fopen(fileneeded[filenum].filename, "wb");
if (!fileneeded[filenum].phandle) I_Error("Can't create file %s: %s",fileneeded[filenum].filename, strerror(errno));
CONS_Printf("\r%s...\n",fileneeded[filenum].filename);
if (fileneeded[filenum].file)
I_Error("Got_Filetxpak: already open file\n");
fileneeded[filenum].file = fopen(fileneeded[filenum].filename, "wb");
if (!fileneeded[filenum].file)
I_Error("Can't create file %s: %s", fileneeded[filenum].filename, strerror(errno));
CONS_Printf("\r%s...\n",fileneeded[filenum].filename);
fileneeded[filenum].currentsize = 0;
fileneeded[filenum].status = FS_DOWNLOADING;
}
@ -664,24 +759,24 @@ void Got_Filetxpak(void)
{
UINT32 pos = LONG(netbuffer->u.filetxpak.position);
UINT16 size = SHORT(netbuffer->u.filetxpak.size);
// use a special tric to know when file is finished (not allways used)
// WARNING: filepak can arrive out of order so don't stop now !
// Use a special trick to know when the file is complete (not always used)
// WARNING: file fragments can arrive out of order so don't stop yet!
if (pos & 0x80000000)
{
pos &= ~0x80000000;
fileneeded[filenum].totalsize = pos + size;
}
// we can receive packet in the wrong order, anyway all os support gaped file
fseek(fileneeded[filenum].phandle,pos,SEEK_SET);
if (fwrite(netbuffer->u.filetxpak.data,size,1,fileneeded[filenum].phandle)!=1)
I_Error("Can't write to %s: %s\n",fileneeded[filenum].filename, strerror(ferror(fileneeded[filenum].phandle)));
// We can receive packet in the wrong order, anyway all os support gaped file
fseek(fileneeded[filenum].file, pos, SEEK_SET);
if (fwrite(netbuffer->u.filetxpak.data,size,1,fileneeded[filenum].file) != 1)
I_Error("Can't write to %s: %s\n",fileneeded[filenum].filename, strerror(ferror(fileneeded[filenum].file)));
fileneeded[filenum].currentsize += size;
// finished?
// Finished?
if (fileneeded[filenum].currentsize == fileneeded[filenum].totalsize)
{
fclose(fileneeded[filenum].phandle);
fileneeded[filenum].phandle = NULL;
fclose(fileneeded[filenum].file);
fileneeded[filenum].file = NULL;
fileneeded[filenum].status = FS_FOUND;
CONS_Printf(M_GetText("Downloading %s...(done)\n"),
fileneeded[filenum].filename);
@ -689,8 +784,8 @@ void Got_Filetxpak(void)
}
else
I_Error("Received a file not requested\n");
// send ack back quickly
// Send ack back quickly
if (++filetime == 3)
{
Net_SendAcks(servernode);
@ -702,33 +797,39 @@ void Got_Filetxpak(void)
#endif
}
void AbortSendFiles(INT32 node)
/** Cancels all file requests for a node
*
* \param node The destination
* \sa SV_EndFileSend
*
*/
void SV_AbortSendFiles(INT32 node)
{
while (transfer[node].txlist)
EndSend(node);
SV_EndFileSend(node);
}
void CloseNetFile(void)
{
INT32 i;
// is sending?
// Is sending?
for (i = 0; i < MAXNETNODES; i++)
AbortSendFiles(i);
SV_AbortSendFiles(i);
// receiving a file?
// Receiving a file?
for (i = 0; i < MAX_WADFILES; i++)
if (fileneeded[i].status == FS_DOWNLOADING && fileneeded[i].phandle)
if (fileneeded[i].status == FS_DOWNLOADING && fileneeded[i].file)
{
fclose(fileneeded[i].phandle);
// file is not complete delete it
fclose(fileneeded[i].file);
// File is not complete delete it
remove(fileneeded[i].filename);
}
// remove FILEFRAGMENT from acknledge list
// Remove PT_FILEFRAGMENT from acknowledge list
Net_AbortPacketType(PT_FILEFRAGMENT);
}
// functions cut and pasted from doomatic :)
// Functions cut and pasted from Doomatic :)
void nameonly(char *s)
{

View file

@ -29,21 +29,21 @@ typedef enum
FS_FOUND,
FS_REQUESTED,
FS_DOWNLOADING,
FS_OPEN, // is opened and used in w_wad
FS_OPEN, // Is opened and used in w_wad
FS_MD5SUMBAD
} filestatus_t;
typedef struct
{
UINT8 important;
UINT8 willsend; // is the server willing to send it?
UINT8 willsend; // Is the server willing to send it?
char filename[MAX_WADPATH];
UINT8 md5sum[16];
// used only for download
FILE *phandle;
// Used only for download
FILE *file;
UINT32 currentsize;
UINT32 totalsize;
filestatus_t status; // the value returned by recsearch
filestatus_t status; // The value returned by recsearch
} fileneeded_t;
extern INT32 fileneedednum;
@ -58,28 +58,24 @@ UINT8 *PutFileNeeded(void);
void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr);
void CL_PrepareDownloadSaveGame(const char *tmpsave);
// check file list in wadfiles return 0 when a file is not found
// 1 if all file are found
// 2 if you cannot connect (different wad version or
// no enought space to download files)
INT32 CL_CheckFiles(void);
void CL_LoadServerFiles(void);
void SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod,
void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod,
UINT8 fileid);
void FiletxTicker(void);
void SV_FileSendTicker(void);
void Got_Filetxpak(void);
boolean CL_CheckDownloadable(void);
boolean CL_SendRequestFile(void);
void Got_RequestFilePak(INT32 node);
void AbortSendFiles(INT32 node);
void SV_AbortSendFiles(INT32 node);
void CloseNetFile(void);
boolean fileexist(char *filename, time_t ptime);
// search a file in the wadpath, return FS_FOUND when found
// Search a file in the wadpath, return FS_FOUND when found
filestatus_t findfile(char *filename, const UINT8 *wantedmd5sum,
boolean completepath);
filestatus_t checkfilemd5(char *filename, const UINT8 *wantedmd5sum);

View file

@ -44,6 +44,7 @@ typedef enum
SF_STOMPDAMAGE = 1<<9, // Always damage enemies, etc by landing on them, no matter your vunerability?
SF_MARIODAMAGE = SF_NOJUMPDAMAGE|SF_STOMPDAMAGE, // The Mario method of being able to damage enemies, etc.
SF_MACHINE = 1<<10, // Beep boop. Are you a robot?
SF_NOSPINDASHDUST = 1<<11, // Don't spawn dust particles when charging a spindash
// free up to and including 1<<31
} skinflags_t;
@ -151,7 +152,13 @@ typedef enum
PF_ANALOGMODE = 1<<26, // Analog mode?
// Can carry another player?
PF_CANCARRY = 1<<27
PF_CANCARRY = 1<<27,
// Used shield ability
PF_SHIELDABILITY = 1<<28,
// Force jump damage?
PF_FORCEJUMPDAMAGE = 1<<29
// free up to and including 1<<31
} pflags_t;
@ -178,23 +185,34 @@ typedef enum
typedef enum
{
SH_NONE = 0,
// Standard shields
SH_JUMP,
SH_ATTRACT,
SH_ELEMENTAL,
SH_BOMB,
// Stupid useless unimplimented Sonic 3 shields
SH_BUBBLEWRAP,
SH_THUNDERCOIN,
SH_FLAMEAURA,
// Pity shield: the world's most basic shield ever, given to players who suck at Match
SH_PITY,
// The fireflower is special, it combines with other shields.
SH_FIREFLOWER = 0x100,
// The force shield uses the lower 8 bits to count how many hits are left.
SH_FORCE = 0x200,
SH_STACK = SH_FIREFLOWER,
// Shield flags
SH_PROTECTFIRE = 0x400,
SH_PROTECTWATER = 0x800,
SH_PROTECTELECTRIC = 0x1000,
// Indivisible shields
SH_PITY = 1, // the world's most basic shield ever, given to players who suck at Match
SH_WHIRLWIND,
SH_ARMAGEDDON,
// normal shields that use flags
SH_ATTRACT = SH_PROTECTELECTRIC,
SH_ELEMENTAL = SH_PROTECTFIRE|SH_PROTECTWATER,
// Sonic 3 shields
SH_FLAMEAURA = SH_PROTECTFIRE,
SH_BUBBLEWRAP = SH_PROTECTWATER,
SH_THUNDERCOIN = SH_WHIRLWIND|SH_PROTECTELECTRIC,
// The force shield uses the lower 8 bits to count how many extra hits are left.
SH_FORCE = 0x100,
SH_FORCEHP = 0xFF, // to be used as a bitmask only
// Mostly for use with Mario mode.
SH_FIREFLOWER = 0x200,
SH_STACK = SH_FIREFLOWER, // second-layer shields
SH_NOSTACK = ~SH_STACK
} shieldtype_t; // pw_shield
@ -359,8 +377,8 @@ typedef struct player_s
UINT8 gotcontinue; // Got continue from this stage?
fixed_t speed; // Player's speed (distance formula of MOMX and MOMY values)
UINT8 jumping; // Jump counter
UINT8 secondjump;
UINT8 jumping; // Holding down jump button
UINT8 secondjump; // Jump counter
UINT8 fly1; // Tails flying
UINT8 scoreadd; // Used for multiple enemy attack bonus

View file

@ -371,7 +371,9 @@ static void clear_levels(void)
// (no need to set num to 0, we're freeing the entire header shortly)
Z_Free(mapheaderinfo[i]->customopts);
P_DeleteFlickies(i);
P_DeleteGrades(i);
Z_Free(mapheaderinfo[i]);
mapheaderinfo[i] = NULL;
}
@ -990,6 +992,34 @@ static const struct {
{NULL, 0}
};
static const struct {
const char *name;
const mobjtype_t type;
} FLICKYTYPES[] = {
{"BLUEBIRD", MT_FLICKY_01},
{"RABBIT", MT_FLICKY_02},
{"CHICKEN", MT_FLICKY_03},
{"SEAL", MT_FLICKY_04},
{"PIG", MT_FLICKY_05},
{"CHIPMUNK", MT_FLICKY_06},
{"PENGUIN", MT_FLICKY_07},
{"FISH", MT_FLICKY_08},
{"RAM", MT_FLICKY_09},
{"PUFFIN", MT_FLICKY_10},
{"COW", MT_FLICKY_11},
{"RAT", MT_FLICKY_12},
{"BEAR", MT_FLICKY_13},
{"DOVE", MT_FLICKY_14},
{"CAT", MT_FLICKY_15},
{"CANARY", MT_FLICKY_16},
{"a", 0}, // End of normal flickies - a lower case character so will never fastcmp valid with uppercase tmp
//{"FLICKER", MT_FLICKER},
{"SEED", MT_SEED},
{NULL, 0}
};
#define MAXFLICKIES 64
static void readlevelheader(MYFILE *f, INT32 num)
{
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
@ -1088,8 +1118,82 @@ static void readlevelheader(MYFILE *f, INT32 num)
// Now go to uppercase
strupr(word2);
// List of flickies that are be freed in this map
if (fastcmp(word, "FLICKYLIST") || fastcmp(word, "ANIMALLIST"))
{
if (fastcmp(word2, "NONE"))
P_DeleteFlickies(num-1);
else if (fastcmp(word2, "DEMO"))
P_SetDemoFlickies(num-1);
else if (fastcmp(word2, "ALL"))
{
mobjtype_t tmpflickies[MAXFLICKIES];
for (mapheaderinfo[num-1]->numFlickies = 0;
((mapheaderinfo[num-1]->numFlickies < MAXFLICKIES) && FLICKYTYPES[mapheaderinfo[num-1]->numFlickies].type);
mapheaderinfo[num-1]->numFlickies++)
tmpflickies[mapheaderinfo[num-1]->numFlickies] = FLICKYTYPES[mapheaderinfo[num-1]->numFlickies].type;
if (mapheaderinfo[num-1]->numFlickies) // just in case...
{
size_t newsize = sizeof(mobjtype_t) * mapheaderinfo[num-1]->numFlickies;
mapheaderinfo[num-1]->flickies = Z_Realloc(mapheaderinfo[num-1]->flickies, newsize, PU_STATIC, NULL);
M_Memcpy(mapheaderinfo[num-1]->flickies, tmpflickies, newsize);
}
}
else
{
mobjtype_t tmpflickies[MAXFLICKIES];
mapheaderinfo[num-1]->numFlickies = 0;
tmp = strtok(word2,",");
// get up to the first MAXFLICKIES flickies
do {
if (mapheaderinfo[num-1]->numFlickies == MAXFLICKIES) // never going to get above that number
{
deh_warning("Level header %d: too many flickies\n", num);
break;
}
if (fastncmp(tmp, "MT_", 3)) // support for specified mobjtypes...
{
i = get_mobjtype(tmp);
if (!i)
{
//deh_warning("Level header %d: unknown flicky mobj type %s\n", num, tmp); -- no need for this line as get_mobjtype complains too
continue;
}
tmpflickies[mapheaderinfo[num-1]->numFlickies] = i;
}
else // ...or a quick, limited selection of default flickies!
{
for (i = 0; FLICKYTYPES[i].name; i++)
if (fastcmp(tmp, FLICKYTYPES[i].name))
break;
if (!FLICKYTYPES[i].name)
{
deh_warning("Level header %d: unknown flicky selection %s\n", num, tmp);
continue;
}
tmpflickies[mapheaderinfo[num-1]->numFlickies] = FLICKYTYPES[i].type;
}
mapheaderinfo[num-1]->numFlickies++;
} while ((tmp = strtok(NULL,",")) != NULL);
if (mapheaderinfo[num-1]->numFlickies)
{
size_t newsize = sizeof(mobjtype_t) * mapheaderinfo[num-1]->numFlickies;
mapheaderinfo[num-1]->flickies = Z_Realloc(mapheaderinfo[num-1]->flickies, newsize, PU_STATIC, NULL);
// now we add them to the list!
M_Memcpy(mapheaderinfo[num-1]->flickies, tmpflickies, newsize);
}
else
deh_warning("Level header %d: no valid flicky types found\n", num);
}
}
// NiGHTS grades
if (fastncmp(word, "GRADES", 6))
else if (fastncmp(word, "GRADES", 6))
{
UINT8 mare = (UINT8)atoi(word + 6);
@ -1322,6 +1426,8 @@ static void readlevelheader(MYFILE *f, INT32 num)
Z_Free(s);
}
#undef MAXFLICKIES
static void readcutscenescene(MYFILE *f, INT32 num, INT32 scenenum)
{
char *s = Z_Calloc(MAXLINELEN, PU_STATIC, NULL);
@ -1687,6 +1793,9 @@ static actionpointer_t actionpointers[] =
{{A_WaterShield}, "A_WATERSHIELD"},
{{A_ForceShield}, "A_FORCESHIELD"},
{{A_PityShield}, "A_PITYSHIELD"},
{{A_FlameShield}, "A_FLAMESHIELD"},
{{A_BubbleShield}, "A_BUBBLESHIELD"},
{{A_ThunderShield}, "A_THUNDERSHIELD"},
{{A_GravityBox}, "A_GRAVITYBOX"},
{{A_ScoreRise}, "A_SCORERISE"},
{{A_ParticleSpawn}, "A_PARTICLESPAWN"},
@ -1842,6 +1951,16 @@ static actionpointer_t actionpointers[] =
{{A_BrakLobShot}, "A_BRAKLOBSHOT"},
{{A_NapalmScatter}, "A_NAPALMSCATTER"},
{{A_SpawnFreshCopy}, "A_SPAWNFRESHCOPY"},
{{A_FlickySpawn}, "A_FLICKYSPAWN"},
{{A_FlickyAim}, "A_FLICKYAIM"},
{{A_FlickyFly}, "A_FLICKYFLY"},
{{A_FlickySoar}, "A_FLICKYSOAR"},
{{A_FlickyCoast}, "A_FLICKYCOAST"},
{{A_FlickyHop}, "A_FLICKYHOP"},
{{A_FlickyFlounder}, "A_FLICKYFLOUNDER"},
{{A_FlickyCheck}, "A_FLICKYCHECK"},
{{A_FlickyHeightCheck}, "A_FLICKYHEIGHTCHECK"},
{{A_FlickyFlutter}, "A_FLICKYFLUTTER"},
{{NULL}, "NONE"},
@ -2778,7 +2897,7 @@ static void readpatch(MYFILE *f, const char *name, UINT16 wad)
char *word2;
char *tmp;
INT32 i = 0, j = 0, value;
texpatch_t patch = {0, 0, UINT16_MAX, UINT16_MAX};
texpatch_t patch = {0, 0, UINT16_MAX, UINT16_MAX, 0};
// Jump to the texture this patch belongs to, which,
// coincidentally, is always the last one on the buffer cache.
@ -3378,11 +3497,11 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
{
if (i == 0 && word2[0] != '0') // If word2 isn't a number
i = get_mobjtype(word2); // find a thing by name
if (i < NUMMOBJTYPES && i >= 0)
if (i < NUMMOBJTYPES && i > 0)
readthing(f, i);
else
{
deh_warning("Thing %d out of range (0 - %d)", i, NUMMOBJTYPES-1);
deh_warning("Thing %d out of range (1 - %d)", i, NUMMOBJTYPES-1);
ignorelines(f);
}
DEH_WriteUndoline(word, word2, UNDO_HEADER);
@ -4834,7 +4953,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_SPIKEBALL7",
"S_SPIKEBALL8",
// Fire Shield's Spawn
// Elemental Shield's Spawn
"S_SPINFIRE1",
"S_SPINFIRE2",
"S_SPINFIRE3",
@ -4908,6 +5027,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_RECYCLER_BOX",
"S_SCORE1K_BOX",
"S_SCORE10K_BOX",
"S_FLAMEAURA_BOX",
"S_BUBBLEWRAP_BOX",
"S_THUNDERCOIN_BOX",
// Gold Repeat Monitor States (one per box)
"S_PITY_GOLDBOX",
@ -4920,6 +5042,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_INVULN_GOLDBOX",
"S_EGGMAN_GOLDBOX",
"S_GRAVITY_GOLDBOX",
"S_FLAMEAURA_GOLDBOX",
"S_BUBBLEWRAP_GOLDBOX",
"S_THUNDERCOIN_GOLDBOX",
// Team Ring Boxes (these are special)
"S_RING_REDBOX1",
@ -4981,6 +5106,15 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_SCORE10K_ICON1",
"S_SCORE10K_ICON2",
"S_FLAMEAURA_ICON1",
"S_FLAMEAURA_ICON2",
"S_BUBBLEWRAP_ICON1",
"S_BUBBLEWRAP_ICON2",
"S_THUNDERCOIN_ICON1",
"S_THUNDERCOIN_ICON2",
"S_ROCKET",
"S_LASER",
@ -5350,6 +5484,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_MAGN10",
"S_MAGN11",
"S_MAGN12",
"S_MAGN13",
"S_FORC1",
"S_FORC2",
@ -5373,6 +5508,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_FORC19",
"S_FORC20",
"S_FORC21",
"S_ELEM1",
"S_ELEM2",
"S_ELEM3",
@ -5386,6 +5523,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_ELEM11",
"S_ELEM12",
"S_ELEM13",
"S_ELEM14",
"S_ELEMF1",
"S_ELEMF2",
"S_ELEMF3",
@ -5394,6 +5534,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_ELEMF6",
"S_ELEMF7",
"S_ELEMF8",
"S_ELEMF9",
"S_ELEMF10",
"S_PITY1",
"S_PITY2",
@ -5406,6 +5548,84 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_PITY9",
"S_PITY10",
"S_FIRS1",
"S_FIRS2",
"S_FIRS3",
"S_FIRS4",
"S_FIRS5",
"S_FIRS6",
"S_FIRS7",
"S_FIRS8",
"S_FIRS9",
"S_FIRS10",
"S_FIRS11",
"S_FIRSB1",
"S_FIRSB2",
"S_FIRSB3",
"S_FIRSB4",
"S_FIRSB5",
"S_FIRSB6",
"S_FIRSB7",
"S_FIRSB8",
"S_FIRSB9",
"S_FIRSB10",
"S_BUBS1",
"S_BUBS2",
"S_BUBS3",
"S_BUBS4",
"S_BUBS5",
"S_BUBS6",
"S_BUBS7",
"S_BUBS8",
"S_BUBS9",
"S_BUBS10",
"S_BUBS11",
"S_BUBSB1",
"S_BUBSB2",
"S_BUBSB3",
"S_BUBSB4",
"S_BUBSB5",
"S_BUBSB6",
"S_ZAPS1",
"S_ZAPS2",
"S_ZAPS3",
"S_ZAPS4",
"S_ZAPS5",
"S_ZAPS6",
"S_ZAPS7",
"S_ZAPS8",
"S_ZAPS9",
"S_ZAPS10",
"S_ZAPS11",
"S_ZAPS12",
"S_ZAPS13", // blank frame
"S_ZAPS14",
"S_ZAPS15",
"S_ZAPS16",
"S_ZAPSB1", // blank frame
"S_ZAPSB2",
"S_ZAPSB3",
"S_ZAPSB4",
"S_ZAPSB5",
"S_ZAPSB6",
"S_ZAPSB7",
"S_ZAPSB8",
"S_ZAPSB9",
"S_ZAPSB10",
"S_ZAPSB11", // blank frame
// Thunder spark
"S_THUNDERCOIN_SPARK",
// Invincibility Sparkles
"S_IVSP",
@ -5416,43 +5636,133 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_SSPK4",
"S_SSPK5",
// Freed Birdie
"S_BIRD1",
"S_BIRD2",
"S_BIRD3",
// Flicky-sized bubble
"S_FLICKY_BUBBLE",
// Freed Bunny
"S_BUNNY1",
"S_BUNNY2",
"S_BUNNY3",
"S_BUNNY4",
"S_BUNNY5",
"S_BUNNY6",
"S_BUNNY7",
"S_BUNNY8",
"S_BUNNY9",
"S_BUNNY10",
// Bluebird
"S_FLICKY_01_OUT",
"S_FLICKY_01_FLAP1",
"S_FLICKY_01_FLAP2",
"S_FLICKY_01_FLAP3",
// Freed Mouse
"S_MOUSE1",
"S_MOUSE2",
// Rabbit
"S_FLICKY_02_OUT",
"S_FLICKY_02_AIM",
"S_FLICKY_02_HOP",
"S_FLICKY_02_UP",
"S_FLICKY_02_DOWN",
// Freed Chicken
"S_CHICKEN1",
"S_CHICKENHOP",
"S_CHICKENFLY1",
"S_CHICKENFLY2",
// Chicken
"S_FLICKY_03_OUT",
"S_FLICKY_03_AIM",
"S_FLICKY_03_HOP",
"S_FLICKY_03_UP",
"S_FLICKY_03_FLAP1",
"S_FLICKY_03_FLAP2",
// Freed Cow
"S_COW1",
"S_COW2",
"S_COW3",
"S_COW4",
// Seal
"S_FLICKY_04_OUT",
"S_FLICKY_04_AIM",
"S_FLICKY_04_HOP",
"S_FLICKY_04_UP",
"S_FLICKY_04_DOWN",
"S_FLICKY_04_SWIM1",
"S_FLICKY_04_SWIM2",
"S_FLICKY_04_SWIM3",
"S_FLICKY_04_SWIM4",
// Red Birdie in Bubble
"S_RBIRD1",
"S_RBIRD2",
"S_RBIRD3",
// Pig
"S_FLICKY_05_OUT",
"S_FLICKY_05_AIM",
"S_FLICKY_05_HOP",
"S_FLICKY_05_UP",
"S_FLICKY_05_DOWN",
// Chipmunk
"S_FLICKY_06_OUT",
"S_FLICKY_06_AIM",
"S_FLICKY_06_HOP",
"S_FLICKY_06_UP",
"S_FLICKY_06_DOWN",
// Penguin
"S_FLICKY_07_OUT",
"S_FLICKY_07_AIML",
"S_FLICKY_07_HOPL",
"S_FLICKY_07_UPL",
"S_FLICKY_07_DOWNL",
"S_FLICKY_07_AIMR",
"S_FLICKY_07_HOPR",
"S_FLICKY_07_UPR",
"S_FLICKY_07_DOWNR",
"S_FLICKY_07_SWIM1",
"S_FLICKY_07_SWIM2",
"S_FLICKY_07_SWIM3",
// Fish
"S_FLICKY_08_OUT",
"S_FLICKY_08_AIM",
"S_FLICKY_08_HOP",
"S_FLICKY_08_FLAP1",
"S_FLICKY_08_FLAP2",
"S_FLICKY_08_FLAP3",
"S_FLICKY_08_FLAP4",
"S_FLICKY_08_SWIM1",
"S_FLICKY_08_SWIM2",
"S_FLICKY_08_SWIM3",
"S_FLICKY_08_SWIM4",
// Ram
"S_FLICKY_09_OUT",
"S_FLICKY_09_AIM",
"S_FLICKY_09_HOP",
"S_FLICKY_09_UP",
"S_FLICKY_09_DOWN",
// Puffin
"S_FLICKY_10_OUT",
"S_FLICKY_10_FLAP1",
"S_FLICKY_10_FLAP2",
// Cow
"S_FLICKY_11_OUT",
"S_FLICKY_11_AIM",
"S_FLICKY_11_RUN1",
"S_FLICKY_11_RUN2",
"S_FLICKY_11_RUN3",
// Rat
"S_FLICKY_12_OUT",
"S_FLICKY_12_AIM",
"S_FLICKY_12_RUN1",
"S_FLICKY_12_RUN2",
"S_FLICKY_12_RUN3",
// Bear
"S_FLICKY_13_OUT",
"S_FLICKY_13_AIM",
"S_FLICKY_13_HOP",
"S_FLICKY_13_UP",
"S_FLICKY_13_DOWN",
// Dove
"S_FLICKY_14_OUT",
"S_FLICKY_14_FLAP1",
"S_FLICKY_14_FLAP2",
"S_FLICKY_14_FLAP3",
// Cat
"S_FLICKY_15_OUT",
"S_FLICKY_15_AIM",
"S_FLICKY_15_HOP",
"S_FLICKY_15_UP",
"S_FLICKY_15_DOWN",
// Canary
"S_FLICKY_16_OUT",
"S_FLICKY_16_FLAP1",
"S_FLICKY_16_FLAP2",
"S_FLICKY_16_FLAP3",
"S_YELLOWSPRING",
"S_YELLOWSPRING2",
@ -5566,6 +5876,20 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_WATERZAP",
// Spindash dust
"S_SPINDUST1",
"S_SPINDUST2",
"S_SPINDUST3",
"S_SPINDUST4",
"S_SPINDUST_BUBBLE1",
"S_SPINDUST_BUBBLE2",
"S_SPINDUST_BUBBLE3",
"S_SPINDUST_BUBBLE4",
"S_SPINDUST_FIRE1",
"S_SPINDUST_FIRE2",
"S_SPINDUST_FIRE3",
"S_SPINDUST_FIRE4",
"S_FOG1",
"S_FOG2",
"S_FOG3",
@ -5938,6 +6262,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_NIGHTOPIANHELPER6",
"S_NIGHTOPIANHELPER7",
"S_NIGHTOPIANHELPER8",
"S_NIGHTOPIANHELPER9",
"S_CRUMBLE1",
"S_CRUMBLE2",
@ -5961,10 +6286,10 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_SPRK16",
// Robot Explosion
"S_XPLD_FLICKY",
"S_XPLD1",
"S_XPLD2",
"S_XPLD3",
"S_XPLD4",
"S_XPLD_EGGTRAP",
// Underwater Explosion
"S_WPLD1",
@ -6164,6 +6489,9 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_RECYCLER_BOX",
"MT_SCORE1K_BOX",
"MT_SCORE10K_BOX",
"MT_FLAMEAURA_BOX",
"MT_BUBBLEWRAP_BOX",
"MT_THUNDERCOIN_BOX",
// Monitor boxes -- repeating (big) boxes
"MT_PITY_GOLDBOX",
@ -6176,6 +6504,9 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_INVULN_GOLDBOX",
"MT_EGGMAN_GOLDBOX",
"MT_GRAVITY_GOLDBOX",
"MT_FLAMEAURA_GOLDBOX",
"MT_BUBBLEWRAP_GOLDBOX",
"MT_THUNDERCOIN_GOLDBOX",
// Monitor boxes -- special
"MT_RING_REDBOX",
@ -6198,6 +6529,9 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_RECYCLER_ICON",
"MT_SCORE1K_ICON",
"MT_SCORE10K_ICON",
"MT_FLAMEAURA_ICON",
"MT_BUBBLEWRAP_ICON",
"MT_THUNDERCOIN_ICON",
// Projectiles
"MT_ROCKET",
@ -6350,22 +6684,36 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_EGGSTATUE2",
// Powerup Indicators
"MT_GREENORB", // Elemental shield mobj
"MT_YELLOWORB", // Attract shield mobj
"MT_BLUEORB", // Force shield mobj
"MT_BLACKORB", // Armageddon shield mobj
"MT_WHITEORB", // Whirlwind shield mobj
"MT_PITYORB", // Pity shield mobj
"MT_IVSP", // invincibility sparkles
"MT_ELEMENTAL_ORB", // Elemental shield mobj
"MT_ATTRACT_ORB", // Attract shield mobj
"MT_FORCE_ORB", // Force shield mobj
"MT_ARMAGEDDON_ORB", // Armageddon shield mobj
"MT_WHIRLWIND_ORB", // Whirlwind shield mobj
"MT_PITY_ORB", // Pity shield mobj
"MT_FLAMEAURA_ORB", // Flame shield mobj
"MT_BUBBLEWRAP_ORB", // Bubble shield mobj
"MT_THUNDERCOIN_ORB", // Thunder shield mobj
"MT_THUNDERCOIN_SPARK", // Thunder spark
"MT_IVSP", // Invincibility sparkles
"MT_SUPERSPARK", // Super Sonic Spark
// Freed Animals
"MT_BIRD", // Birdie freed!
"MT_BUNNY", // Bunny freed!
"MT_MOUSE", // Mouse
"MT_CHICKEN", // Chicken
"MT_COW", // Cow
"MT_REDBIRD", // Red Birdie in Bubble
// Flickies
"MT_FLICKY_01", // Bluebird
"MT_FLICKY_02", // Rabbit
"MT_FLICKY_03", // Chicken
"MT_FLICKY_04", // Seal
"MT_FLICKY_05", // Pig
"MT_FLICKY_06", // Chipmunk
"MT_FLICKY_07", // Penguin
"MT_FLICKY_08", // Fish
"MT_FLICKY_09", // Ram
"MT_FLICKY_10", // Puffin
"MT_FLICKY_11", // Cow
"MT_FLICKY_12", // Rat
"MT_FLICKY_13", // Bear
"MT_FLICKY_14", // Dove
"MT_FLICKY_15", // Cat
"MT_FLICKY_16", // Canary
// Environmental Effects
"MT_RAIN", // Rain
@ -6376,6 +6724,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_MEDIUMBUBBLE", // medium bubble
"MT_EXTRALARGEBUBBLE", // extra large bubble
"MT_WATERZAP",
"MT_SPINDUST", // Spindash dust
"MT_TFOG",
"MT_SEED",
"MT_PARTICLE",
@ -6544,35 +6893,36 @@ static const char *const MOBJFLAG_LIST[] = {
// \tMF2_(\S+).*// (.+) --> \t"\1", // \2
static const char *const MOBJFLAG2_LIST[] = {
"AXIS", // It's a NiGHTS axis! (For faster checking)
"TWOD", // Moves like it's in a 2D level
"DONTRESPAWN", // Don't respawn this object!
"DONTDRAW", // Don't generate a vissprite
"AUTOMATIC", // Thrown ring has automatic properties
"RAILRING", // Thrown ring has rail properties
"BOUNCERING", // Thrown ring has bounce properties
"EXPLOSION", // Thrown ring has explosive properties
"SCATTER", // Thrown ring has scatter properties
"BEYONDTHEGRAVE",// Source of this missile has died and has since respawned.
"SLIDEPUSH", // MF_PUSHABLE that pushes continuously.
"CLASSICPUSH", // Drops straight down when object has negative Z.
"STANDONME", // While not pushable, stand on me anyway.
"INFLOAT", // Floating to a height for a move, don't auto float to target's height.
"DEBRIS", // Splash ring from explosion ring
"NIGHTSPULL", // Attracted from a paraloop
"JUSTATTACKED", // can be pushed by other moving mobjs
"FIRING", // turret fire
"SUPERFIRE", // Firing something with Super Sonic-stopping properties. Or, if mobj has MF_MISSILE, this is the actual fire from it.
"SHADOW", // Fuzzy draw, makes targeting harder.
"STRONGBOX", // Flag used for "strong" random monitors.
"OBJECTFLIP", // Flag for objects that always have flipped gravity.
"SKULLFLY", // Special handling: skull in flight.
"FRET", // Flashing from a previous hit
"BOSSNOTRAP", // No Egg Trap after boss
"BOSSFLEE", // Boss is fleeing!
"BOSSDEAD", // Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.)
"AMBUSH", // Alternate behaviour typically set by MTF_AMBUSH
"LINKDRAW", // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position)
"AXIS", // It's a NiGHTS axis! (For faster checking)
"TWOD", // Moves like it's in a 2D level
"DONTRESPAWN", // Don't respawn this object!
"DONTDRAW", // Don't generate a vissprite
"AUTOMATIC", // Thrown ring has automatic properties
"RAILRING", // Thrown ring has rail properties
"BOUNCERING", // Thrown ring has bounce properties
"EXPLOSION", // Thrown ring has explosive properties
"SCATTER", // Thrown ring has scatter properties
"BEYONDTHEGRAVE", // Source of this missile has died and has since respawned.
"SLIDEPUSH", // MF_PUSHABLE that pushes continuously.
"CLASSICPUSH", // Drops straight down when object has negative Z.
"STANDONME", // While not pushable, stand on me anyway.
"INFLOAT", // Floating to a height for a move, don't auto float to target's height.
"DEBRIS", // Splash ring from explosion ring
"NIGHTSPULL", // Attracted from a paraloop
"JUSTATTACKED", // can be pushed by other moving mobjs
"FIRING", // turret fire
"SUPERFIRE", // Firing something with Super Sonic-stopping properties. Or, if mobj has MF_MISSILE, this is the actual fire from it.
"SHADOW", // Fuzzy draw, makes targeting harder.
"STRONGBOX", // Flag used for "strong" random monitors.
"OBJECTFLIP", // Flag for objects that always have flipped gravity.
"SKULLFLY", // Special handling: skull in flight.
"FRET", // Flashing from a previous hit
"BOSSNOTRAP", // No Egg Trap after boss
"BOSSFLEE", // Boss is fleeing!
"BOSSDEAD", // Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.)
"AMBUSH", // Alternate behaviour typically set by MTF_AMBUSH
"LINKDRAW", // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position)
"SHIELD", // Thinker calls P_AddShield/P_ShieldLook (must be partnered with MF_SCENERY to use)
NULL
};
@ -6654,6 +7004,9 @@ static const char *const PLAYERFLAG_LIST[] = {
/*** misc ***/
"FORCESTRAFE", // Translate turn inputs into strafe inputs
"ANALOGMODE", // Analog mode?
"CANCARRY", // Can carry?
"SHIELDABILITY", // Thokked with shield ability
"FORCEJUMPDAMAGE", // Force jump damage
NULL // stop loop here.
};
@ -6998,20 +7351,27 @@ struct {
{"PRECIP_STORM_NOSTRIKES",PRECIP_STORM_NOSTRIKES},
// Shields
// These ones use the lower 8 bits
{"SH_NONE",SH_NONE},
{"SH_JUMP",SH_JUMP},
// Shield flags
{"SH_PROTECTFIRE",SH_PROTECTFIRE},
{"SH_PROTECTWATER",SH_PROTECTWATER},
{"SH_PROTECTELECTRIC",SH_PROTECTELECTRIC},
// Indivisible shields
{"SH_PITY",SH_PITY},
{"SH_WHIRLWIND",SH_WHIRLWIND},
{"SH_ARMAGEDDON",SH_ARMAGEDDON},
// normal shields that use flags
{"SH_ATTRACT",SH_ATTRACT},
{"SH_ELEMENTAL",SH_ELEMENTAL},
{"SH_BOMB",SH_BOMB},
// Sonic 3 shields
{"SH_FLAMEAURA",SH_FLAMEAURA},
{"SH_BUBBLEWRAP",SH_BUBBLEWRAP},
{"SH_THUNDERCOIN",SH_THUNDERCOIN},
{"SH_FLAMEAURA",SH_FLAMEAURA},
{"SH_PITY",SH_PITY},
// These ones are special and use the upper bits
{"SH_FIREFLOWER",SH_FIREFLOWER}, // Lower bits are a normal shield stacked on top of the fire flower
{"SH_FORCE",SH_FORCE}, // Lower bits are how many hits left, 0 is the last hit
// Stack masks
// The force shield uses the lower 8 bits to count how many extra hits are left.
{"SH_FORCE",SH_FORCE},
{"SH_FORCEHP",SH_FORCEHP}, // to be used as a bitmask only
// Mostly for use with Mario mode.
{"SH_FIREFLOWER", SH_FIREFLOWER},
{"SH_STACK",SH_STACK},
{"SH_NOSTACK",SH_NOSTACK},
@ -7045,6 +7405,7 @@ struct {
{"SF_STOMPDAMAGE",SF_STOMPDAMAGE},
{"SF_MARIODAMAGE",SF_MARIODAMAGE},
{"SF_MACHINE",SF_MACHINE},
{"SF_NOSPINDASHDUST",SF_NOSPINDASHDUST},
// Character abilities!
// Primary
@ -7367,7 +7728,7 @@ static mobjtype_t get_mobjtype(const char *word)
if (fastcmp(word, MOBJTYPE_LIST[i]+3))
return i;
deh_warning("Couldn't find mobjtype named 'MT_%s'",word);
return MT_BLUECRAWLA;
return MT_NULL;
}
static statenum_t get_state(const char *word)

View file

@ -407,6 +407,7 @@ void M_StartupLocale(void);
extern void *(*M_Memcpy)(void* dest, const void* src, size_t n) FUNCNONNULL;
char *va(const char *format, ...) FUNCPRINTF;
char *M_GetToken(const char *inputString);
void M_UnGetToken(void);
char *sizeu1(size_t num);
char *sizeu2(size_t num);
char *sizeu3(size_t num);

View file

@ -241,6 +241,10 @@ typedef struct
UINT8 levelflags; ///< LF_flags: merged eight booleans into one UINT8 for space, see below
UINT8 menuflags; ///< LF2_flags: options that affect record attack / nights mode menus
// Freed animals stuff.
UINT8 numFlickies; ///< Internal. For freed flicky support.
mobjtype_t *flickies; ///< List of freeable flickies in this level. Allocated dynamically for space reasons. Be careful.
// NiGHTS stuff.
UINT8 numGradedMares; ///< Internal. For grade support.
nightsgrades_t *grades; ///< NiGHTS grades. Allocated dynamically for space reasons. Be careful.

View file

@ -4728,7 +4728,7 @@ void G_BeginRecording(void)
// Don't do it.
WRITEFIXED(demo_p, player->jumpfactor);
// Save netvar data (SONICCD, etc)
// Save netvar data
CV_SaveNetVars(&demo_p);
memset(&oldcmd,0,sizeof(oldcmd));

View file

@ -271,6 +271,9 @@ light_t *t_lspr[NUMSPRITES] =
&lspr[NOLIGHT], // SPR_TVRC
&lspr[NOLIGHT], // SPR_TV1K
&lspr[NOLIGHT], // SPR_TVTK
&lspr[NOLIGHT], // SPR_TVFL
&lspr[NOLIGHT], // SPR_TVBB
&lspr[NOLIGHT], // SPR_TVZP
// Projectiles
&lspr[NOLIGHT], // SPR_MISL
@ -359,18 +362,32 @@ light_t *t_lspr[NUMSPRITES] =
&lspr[NOLIGHT], // SPR_ELEM
&lspr[NOLIGHT], // SPR_FORC
&lspr[NOLIGHT], // SPR_PITY
&lspr[NOLIGHT], // SPR_FIRS
&lspr[NOLIGHT], // SPR_BUBS
&lspr[NOLIGHT], // SPR_ZAPS
&lspr[INVINCIBLE_L], // SPR_IVSP
&lspr[SUPERSPARK_L], // SPR_SSPK
&lspr[NOLIGHT], // SPR_GOAL
// Freed Animals
&lspr[NOLIGHT], // SPR_BIRD
&lspr[NOLIGHT], // SPR_BUNY
&lspr[NOLIGHT], // SPR_MOUS
&lspr[NOLIGHT], // SPR_CHIC
&lspr[NOLIGHT], // SPR_COWZ
&lspr[NOLIGHT], // SPR_RBRD
// Flickies
&lspr[NOLIGHT], // SPR_FBUB
&lspr[NOLIGHT], // SPR_FL01
&lspr[NOLIGHT], // SPR_FL02
&lspr[NOLIGHT], // SPR_FL03
&lspr[NOLIGHT], // SPR_FL04
&lspr[NOLIGHT], // SPR_FL05
&lspr[NOLIGHT], // SPR_FL06
&lspr[NOLIGHT], // SPR_FL07
&lspr[NOLIGHT], // SPR_FL08
&lspr[NOLIGHT], // SPR_FL09
&lspr[NOLIGHT], // SPR_FL10
&lspr[NOLIGHT], // SPR_FL11
&lspr[NOLIGHT], // SPR_FL12
&lspr[NOLIGHT], // SPR_FL13
&lspr[NOLIGHT], // SPR_FL14
&lspr[NOLIGHT], // SPR_FL15
&lspr[NOLIGHT], // SPR_FL16
// Springs
&lspr[NOLIGHT], // SPR_SPRY

View file

@ -1558,6 +1558,7 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
if (gr_backsector)
{
INT32 gr_toptexture, gr_bottomtexture;
// two sided line
if (gr_backsector->heightsec != -1)
{
@ -1608,19 +1609,22 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
#endif
}
gr_toptexture = R_GetTextureNum(gr_sidedef->toptexture);
gr_bottomtexture = R_GetTextureNum(gr_sidedef->bottomtexture);
// check TOP TEXTURE
if ((
#ifdef ESLOPE
worldhighslope < worldtopslope ||
#endif
worldhigh < worldtop
) && texturetranslation[gr_sidedef->toptexture])
) && gr_toptexture)
{
if (drawtextured)
{
fixed_t texturevpegtop; // top
grTex = HWR_GetTexture(texturetranslation[gr_sidedef->toptexture]);
grTex = HWR_GetTexture(gr_toptexture);
// PEGGING
if (gr_linedef->flags & ML_DONTPEGTOP)
@ -1638,7 +1642,7 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
texturevpegtop += gr_sidedef->rowoffset;
// This is so that it doesn't overflow and screw up the wall, it doesn't need to go higher than the texture's height anyway
texturevpegtop %= SHORT(textures[texturetranslation[gr_sidedef->toptexture]]->height)<<FRACBITS;
texturevpegtop %= SHORT(textures[gr_toptexture]->height)<<FRACBITS;
wallVerts[3].t = wallVerts[2].t = texturevpegtop * grTex->scaleY;
wallVerts[0].t = wallVerts[1].t = (texturevpegtop + gr_frontsector->ceilingheight - gr_backsector->ceilingheight) * grTex->scaleY;
@ -1683,9 +1687,9 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
#endif
if (gr_frontsector->numlights)
HWR_SplitWall(gr_frontsector, wallVerts, texturetranslation[gr_sidedef->toptexture], &Surf, FF_CUTSOLIDS);
HWR_SplitWall(gr_frontsector, wallVerts, gr_toptexture, &Surf, FF_CUTSOLIDS);
else if (grTex->mipmap.flags & TF_TRANSPARENT)
HWR_AddTransparentWall(wallVerts, &Surf, texturetranslation[gr_sidedef->toptexture], PF_Environment, false, lightnum, colormap);
HWR_AddTransparentWall(wallVerts, &Surf, gr_toptexture, PF_Environment, false, lightnum, colormap);
else
HWR_ProjectWall(wallVerts, &Surf, PF_Masked, lightnum, colormap);
}
@ -1695,13 +1699,13 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
#ifdef ESLOPE
worldlowslope > worldbottomslope ||
#endif
worldlow > worldbottom) && texturetranslation[gr_sidedef->bottomtexture]) //only if VISIBLE!!!
worldlow > worldbottom) && gr_bottomtexture) //only if VISIBLE!!!
{
if (drawtextured)
{
fixed_t texturevpegbottom = 0; // bottom
grTex = HWR_GetTexture(texturetranslation[gr_sidedef->bottomtexture]);
grTex = HWR_GetTexture(gr_bottomtexture);
// PEGGING
#ifdef ESLOPE
@ -1721,7 +1725,7 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
texturevpegbottom += gr_sidedef->rowoffset;
// This is so that it doesn't overflow and screw up the wall, it doesn't need to go higher than the texture's height anyway
texturevpegbottom %= SHORT(textures[texturetranslation[gr_sidedef->bottomtexture]]->height)<<FRACBITS;
texturevpegbottom %= SHORT(textures[gr_bottomtexture]->height)<<FRACBITS;
wallVerts[3].t = wallVerts[2].t = texturevpegbottom * grTex->scaleY;
wallVerts[0].t = wallVerts[1].t = (texturevpegbottom + gr_backsector->floorheight - gr_frontsector->floorheight) * grTex->scaleY;
@ -1766,13 +1770,13 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
#endif
if (gr_frontsector->numlights)
HWR_SplitWall(gr_frontsector, wallVerts, texturetranslation[gr_sidedef->bottomtexture], &Surf, FF_CUTSOLIDS);
HWR_SplitWall(gr_frontsector, wallVerts, gr_bottomtexture, &Surf, FF_CUTSOLIDS);
else if (grTex->mipmap.flags & TF_TRANSPARENT)
HWR_AddTransparentWall(wallVerts, &Surf, texturetranslation[gr_sidedef->bottomtexture], PF_Environment, false, lightnum, colormap);
HWR_AddTransparentWall(wallVerts, &Surf, gr_bottomtexture, PF_Environment, false, lightnum, colormap);
else
HWR_ProjectWall(wallVerts, &Surf, PF_Masked, lightnum, colormap);
}
gr_midtexture = texturetranslation[gr_sidedef->midtexture];
gr_midtexture = R_GetTextureNum(gr_sidedef->midtexture);
if (gr_midtexture)
{
FBITFIELD blendmode;
@ -2134,7 +2138,7 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
else
{
// Single sided line... Deal only with the middletexture (if one exists)
gr_midtexture = texturetranslation[gr_sidedef->midtexture];
gr_midtexture = R_GetTextureNum(gr_sidedef->midtexture);
if (gr_midtexture)
{
if (drawtextured)
@ -2232,13 +2236,13 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
if (*rover->topheight < lowcut || *rover->bottomheight > highcut)
continue;
texnum = texturetranslation[sides[rover->master->sidenum[0]].midtexture];
texnum = R_GetTextureNum(sides[rover->master->sidenum[0]].midtexture);
if (rover->master->flags & ML_TFERLINE)
{
size_t linenum = gr_curline->linedef-gr_backsector->lines[0];
newline = rover->master->frontsector->lines[0] + linenum;
texnum = texturetranslation[sides[newline->sidenum[0]].midtexture];
texnum = R_GetTextureNum(sides[newline->sidenum[0]].midtexture);
}
#ifdef ESLOPE
@ -2366,13 +2370,13 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
if (*rover->topheight < lowcut || *rover->bottomheight > highcut)
continue;
texnum = texturetranslation[sides[rover->master->sidenum[0]].midtexture];
texnum = R_GetTextureNum(sides[rover->master->sidenum[0]].midtexture);
if (rover->master->flags & ML_TFERLINE)
{
size_t linenum = gr_curline->linedef-gr_backsector->lines[0];
newline = rover->master->frontsector->lines[0] + linenum;
texnum = texturetranslation[sides[newline->sidenum[0]].midtexture];
texnum = R_GetTextureNum(sides[newline->sidenum[0]].midtexture);
}
#ifdef ESLOPE //backsides
h = *rover->t_slope ? P_GetZAt(*rover->t_slope, v1x, v1y) : *rover->topheight;
@ -4519,8 +4523,8 @@ static void HWR_SortVisSprites(void)
gr_vissprite_t *ds, *dsprev, *dsnext, *dsfirst;
gr_vissprite_t *best = NULL;
gr_vissprite_t unsorted;
float bestdist;
INT32 bestdispoffset;
float bestdist = 0.0f;
INT32 bestdispoffset = 0;
if (!gr_visspritecount)
return;

View file

@ -790,6 +790,14 @@ boolean HU_Responder(event_t *ev)
}
else // if chat_on
{
// Ignore modifier keys
// Note that we do this here so users can still set
// their chat keys to one of these, if they so desire.
if (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT
|| ev->data1 == KEY_LCTRL || ev->data1 == KEY_RCTRL
|| ev->data1 == KEY_LALT || ev->data1 == KEY_RALT)
return true;
c = (UINT8)ev->data1;
// use console translations

1349
src/info.c

File diff suppressed because it is too large Load diff

View file

@ -56,6 +56,9 @@ void A_BombShield(); // Obtained Bomb Shield
void A_WaterShield(); // Obtained Water Shield
void A_ForceShield(); // Obtained Force Shield
void A_PityShield(); // Obtained Pity Shield. We're... sorry.
void A_FlameShield(); // Obtained Flame Shield
void A_BubbleShield(); // Obtained Bubble Shield
void A_ThunderShield(); // Obtained Thunder Shield
void A_GravityBox();
void A_ScoreRise(); // Rise the score logo
void A_ParticleSpawn();
@ -211,6 +214,16 @@ void A_BrakFireShot();
void A_BrakLobShot();
void A_NapalmScatter();
void A_SpawnFreshCopy();
void A_FlickySpawn();
void A_FlickyAim();
void A_FlickyFly();
void A_FlickySoar();
void A_FlickyCoast();
void A_FlickyHop();
void A_FlickyFlounder();
void A_FlickyCheck();
void A_FlickyHeightCheck();
void A_FlickyFlutter();
// ratio of states to sprites to mobj types is roughly 6 : 1 : 1
#define NUMMOBJFREESLOTS 256
@ -351,6 +364,9 @@ typedef enum sprite
SPR_TVRC, // ReCycler
SPR_TV1K, // 1,000 points (1 K)
SPR_TVTK, // 10,000 points (Ten K)
SPR_TVFL, // FLame shield
SPR_TVBB, // BuBble shield
SPR_TVZP, // Thunder shield (ZaP)
// Projectiles
SPR_MISL,
@ -435,18 +451,32 @@ typedef enum sprite
SPR_ELEM, // Elemental Shield Orb and Fire
SPR_FORC, // Force Shield Orb
SPR_PITY, // Pity Shield Orb
SPR_FIRS, // Flame Shield Orb
SPR_BUBS, // Bubble Shield Orb
SPR_ZAPS, // Thunder Shield Orb
SPR_IVSP, // invincibility sparkles
SPR_SSPK, // Super Sonic Spark
SPR_GOAL, // Special Stage goal (here because lol NiGHTS)
// Freed Animals
SPR_BIRD, // Birdie freed!
SPR_BUNY, // Bunny freed!
SPR_MOUS, // Mouse
SPR_CHIC, // Chicken
SPR_COWZ, // Cow
SPR_RBRD, // Red Birdie in Bubble
// Flickies
SPR_FBUB, // Flicky-sized bubble
SPR_FL01, // Bluebird
SPR_FL02, // Rabbit
SPR_FL03, // Chicken
SPR_FL04, // Seal
SPR_FL05, // Pig
SPR_FL06, // Chipmunk
SPR_FL07, // Penguin
SPR_FL08, // Fish
SPR_FL09, // Ram
SPR_FL10, // Puffin
SPR_FL11, // Cow
SPR_FL12, // Rat
SPR_FL13, // Bear
SPR_FL14, // Dove
SPR_FL15, // Cat
SPR_FL16, // Canary
// Springs
SPR_SPRY, // yellow spring
@ -466,6 +496,8 @@ typedef enum sprite
SPR_SMOK,
SPR_BUBL, // Bubble
SPR_WZAP,
SPR_DUST, // Spindash dust
SPR_FPRT, // Spindash dust (flame)
SPR_TFOG, // Teleport Fog
SPR_SEED, // Sonic CD flower seed
SPR_PRTL, // Particle (for fans, etc.)
@ -1723,7 +1755,7 @@ typedef enum state
S_SPIKEBALL7,
S_SPIKEBALL8,
// Fire Shield's Spawn
// Elemental Shield's Spawn
S_SPINFIRE1,
S_SPINFIRE2,
S_SPINFIRE3,
@ -1797,6 +1829,9 @@ typedef enum state
S_RECYCLER_BOX,
S_SCORE1K_BOX,
S_SCORE10K_BOX,
S_FLAMEAURA_BOX,
S_BUBBLEWRAP_BOX,
S_THUNDERCOIN_BOX,
// Gold Repeat Monitor States (one per box)
S_PITY_GOLDBOX,
@ -1809,6 +1844,9 @@ typedef enum state
S_INVULN_GOLDBOX,
S_EGGMAN_GOLDBOX,
S_GRAVITY_GOLDBOX,
S_FLAMEAURA_GOLDBOX,
S_BUBBLEWRAP_GOLDBOX,
S_THUNDERCOIN_GOLDBOX,
// Team Ring Boxes (these are special)
S_RING_REDBOX1,
@ -1870,6 +1908,15 @@ typedef enum state
S_SCORE10K_ICON1,
S_SCORE10K_ICON2,
S_FLAMEAURA_ICON1,
S_FLAMEAURA_ICON2,
S_BUBBLEWRAP_ICON1,
S_BUBBLEWRAP_ICON2,
S_THUNDERCOIN_ICON1,
S_THUNDERCOIN_ICON2,
// ---
S_ROCKET,
@ -2241,6 +2288,7 @@ typedef enum state
S_MAGN10,
S_MAGN11,
S_MAGN12,
S_MAGN13,
S_FORC1,
S_FORC2,
@ -2264,6 +2312,8 @@ typedef enum state
S_FORC19,
S_FORC20,
S_FORC21,
S_ELEM1,
S_ELEM2,
S_ELEM3,
@ -2277,6 +2327,9 @@ typedef enum state
S_ELEM11,
S_ELEM12,
S_ELEM13,
S_ELEM14,
S_ELEMF1,
S_ELEMF2,
S_ELEMF3,
@ -2285,6 +2338,8 @@ typedef enum state
S_ELEMF6,
S_ELEMF7,
S_ELEMF8,
S_ELEMF9,
S_ELEMF10,
S_PITY1,
S_PITY2,
@ -2297,6 +2352,84 @@ typedef enum state
S_PITY9,
S_PITY10,
S_FIRS1,
S_FIRS2,
S_FIRS3,
S_FIRS4,
S_FIRS5,
S_FIRS6,
S_FIRS7,
S_FIRS8,
S_FIRS9,
S_FIRS10,
S_FIRS11,
S_FIRSB1,
S_FIRSB2,
S_FIRSB3,
S_FIRSB4,
S_FIRSB5,
S_FIRSB6,
S_FIRSB7,
S_FIRSB8,
S_FIRSB9,
S_FIRSB10,
S_BUBS1,
S_BUBS2,
S_BUBS3,
S_BUBS4,
S_BUBS5,
S_BUBS6,
S_BUBS7,
S_BUBS8,
S_BUBS9,
S_BUBS10,
S_BUBS11,
S_BUBSB1,
S_BUBSB2,
S_BUBSB3,
S_BUBSB4,
S_BUBSB5,
S_BUBSB6,
S_ZAPS1,
S_ZAPS2,
S_ZAPS3,
S_ZAPS4,
S_ZAPS5,
S_ZAPS6,
S_ZAPS7,
S_ZAPS8,
S_ZAPS9,
S_ZAPS10,
S_ZAPS11,
S_ZAPS12,
S_ZAPS13, // blank frame
S_ZAPS14,
S_ZAPS15,
S_ZAPS16,
S_ZAPSB1, // blank frame
S_ZAPSB2,
S_ZAPSB3,
S_ZAPSB4,
S_ZAPSB5,
S_ZAPSB6,
S_ZAPSB7,
S_ZAPSB8,
S_ZAPSB9,
S_ZAPSB10,
S_ZAPSB11, // blank frame
//Thunder spark
S_THUNDERCOIN_SPARK,
// Invincibility Sparkles
S_IVSP,
@ -2307,43 +2440,133 @@ typedef enum state
S_SSPK4,
S_SSPK5,
// Freed Birdie
S_BIRD1,
S_BIRD2,
S_BIRD3,
// Flicky-sized bubble
S_FLICKY_BUBBLE,
// Freed Bunny
S_BUNNY1,
S_BUNNY2,
S_BUNNY3,
S_BUNNY4,
S_BUNNY5,
S_BUNNY6,
S_BUNNY7,
S_BUNNY8,
S_BUNNY9,
S_BUNNY10,
// Bluebird
S_FLICKY_01_OUT,
S_FLICKY_01_FLAP1,
S_FLICKY_01_FLAP2,
S_FLICKY_01_FLAP3,
// Freed Mouse
S_MOUSE1,
S_MOUSE2,
// Rabbit
S_FLICKY_02_OUT,
S_FLICKY_02_AIM,
S_FLICKY_02_HOP,
S_FLICKY_02_UP,
S_FLICKY_02_DOWN,
// Freed Chicken
S_CHICKEN1,
S_CHICKENHOP,
S_CHICKENFLY1,
S_CHICKENFLY2,
// Chicken
S_FLICKY_03_OUT,
S_FLICKY_03_AIM,
S_FLICKY_03_HOP,
S_FLICKY_03_UP,
S_FLICKY_03_FLAP1,
S_FLICKY_03_FLAP2,
// Freed Cow
S_COW1,
S_COW2,
S_COW3,
S_COW4,
// Seal
S_FLICKY_04_OUT,
S_FLICKY_04_AIM,
S_FLICKY_04_HOP,
S_FLICKY_04_UP,
S_FLICKY_04_DOWN,
S_FLICKY_04_SWIM1,
S_FLICKY_04_SWIM2,
S_FLICKY_04_SWIM3,
S_FLICKY_04_SWIM4,
// Red Birdie in Bubble
S_RBIRD1,
S_RBIRD2,
S_RBIRD3,
// Pig
S_FLICKY_05_OUT,
S_FLICKY_05_AIM,
S_FLICKY_05_HOP,
S_FLICKY_05_UP,
S_FLICKY_05_DOWN,
// Chipmunk
S_FLICKY_06_OUT,
S_FLICKY_06_AIM,
S_FLICKY_06_HOP,
S_FLICKY_06_UP,
S_FLICKY_06_DOWN,
// Penguin
S_FLICKY_07_OUT,
S_FLICKY_07_AIML,
S_FLICKY_07_HOPL,
S_FLICKY_07_UPL,
S_FLICKY_07_DOWNL,
S_FLICKY_07_AIMR,
S_FLICKY_07_HOPR,
S_FLICKY_07_UPR,
S_FLICKY_07_DOWNR,
S_FLICKY_07_SWIM1,
S_FLICKY_07_SWIM2,
S_FLICKY_07_SWIM3,
// Fish
S_FLICKY_08_OUT,
S_FLICKY_08_AIM,
S_FLICKY_08_HOP,
S_FLICKY_08_FLAP1,
S_FLICKY_08_FLAP2,
S_FLICKY_08_FLAP3,
S_FLICKY_08_FLAP4,
S_FLICKY_08_SWIM1,
S_FLICKY_08_SWIM2,
S_FLICKY_08_SWIM3,
S_FLICKY_08_SWIM4,
// Ram
S_FLICKY_09_OUT,
S_FLICKY_09_AIM,
S_FLICKY_09_HOP,
S_FLICKY_09_UP,
S_FLICKY_09_DOWN,
// Puffin
S_FLICKY_10_OUT,
S_FLICKY_10_FLAP1,
S_FLICKY_10_FLAP2,
// Cow
S_FLICKY_11_OUT,
S_FLICKY_11_AIM,
S_FLICKY_11_RUN1,
S_FLICKY_11_RUN2,
S_FLICKY_11_RUN3,
// Rat
S_FLICKY_12_OUT,
S_FLICKY_12_AIM,
S_FLICKY_12_RUN1,
S_FLICKY_12_RUN2,
S_FLICKY_12_RUN3,
// Bear
S_FLICKY_13_OUT,
S_FLICKY_13_AIM,
S_FLICKY_13_HOP,
S_FLICKY_13_UP,
S_FLICKY_13_DOWN,
// Dove
S_FLICKY_14_OUT,
S_FLICKY_14_FLAP1,
S_FLICKY_14_FLAP2,
S_FLICKY_14_FLAP3,
// Cat
S_FLICKY_15_OUT,
S_FLICKY_15_AIM,
S_FLICKY_15_HOP,
S_FLICKY_15_UP,
S_FLICKY_15_DOWN,
// Canary
S_FLICKY_16_OUT,
S_FLICKY_16_FLAP1,
S_FLICKY_16_FLAP2,
S_FLICKY_16_FLAP3,
S_YELLOWSPRING,
S_YELLOWSPRING2,
@ -2457,6 +2680,20 @@ typedef enum state
S_WATERZAP,
// Spindash dust
S_SPINDUST1,
S_SPINDUST2,
S_SPINDUST3,
S_SPINDUST4,
S_SPINDUST_BUBBLE1,
S_SPINDUST_BUBBLE2,
S_SPINDUST_BUBBLE3,
S_SPINDUST_BUBBLE4,
S_SPINDUST_FIRE1,
S_SPINDUST_FIRE2,
S_SPINDUST_FIRE3,
S_SPINDUST_FIRE4,
S_FOG1,
S_FOG2,
S_FOG3,
@ -2829,6 +3066,7 @@ typedef enum state
S_NIGHTOPIANHELPER6,
S_NIGHTOPIANHELPER7,
S_NIGHTOPIANHELPER8,
S_NIGHTOPIANHELPER9,
S_CRUMBLE1,
S_CRUMBLE2,
@ -2852,10 +3090,10 @@ typedef enum state
S_SPRK16,
// Robot Explosion
S_XPLD_FLICKY,
S_XPLD1,
S_XPLD2,
S_XPLD3,
S_XPLD4,
S_XPLD_EGGTRAP,
// Underwater Explosion
S_WPLD1,
@ -3074,6 +3312,9 @@ typedef enum mobj_type
MT_RECYCLER_BOX,
MT_SCORE1K_BOX,
MT_SCORE10K_BOX,
MT_FLAMEAURA_BOX,
MT_BUBBLEWRAP_BOX,
MT_THUNDERCOIN_BOX,
// Monitor boxes -- repeating (big) boxes
MT_PITY_GOLDBOX,
@ -3086,6 +3327,9 @@ typedef enum mobj_type
MT_INVULN_GOLDBOX,
MT_EGGMAN_GOLDBOX,
MT_GRAVITY_GOLDBOX,
MT_FLAMEAURA_GOLDBOX,
MT_BUBBLEWRAP_GOLDBOX,
MT_THUNDERCOIN_GOLDBOX,
// Monitor boxes -- special
MT_RING_REDBOX,
@ -3108,6 +3352,9 @@ typedef enum mobj_type
MT_RECYCLER_ICON,
MT_SCORE1K_ICON,
MT_SCORE10K_ICON,
MT_FLAMEAURA_ICON,
MT_BUBBLEWRAP_ICON,
MT_THUNDERCOIN_ICON,
// Projectiles
MT_ROCKET,
@ -3260,22 +3507,36 @@ typedef enum mobj_type
MT_EGGSTATUE2,
// Powerup Indicators
MT_GREENORB, // Elemental shield mobj
MT_YELLOWORB, // Attract shield mobj
MT_BLUEORB, // Force shield mobj
MT_BLACKORB, // Armageddon shield mobj
MT_WHITEORB, // Whirlwind shield mobj
MT_PITYORB, // Pity shield mobj
MT_IVSP, // invincibility sparkles
MT_ELEMENTAL_ORB, // Elemental shield mobj
MT_ATTRACT_ORB, // Attract shield mobj
MT_FORCE_ORB, // Force shield mobj
MT_ARMAGEDDON_ORB, // Armageddon shield mobj
MT_WHIRLWIND_ORB, // Whirlwind shield mobj
MT_PITY_ORB, // Pity shield mobj
MT_FLAMEAURA_ORB, // Flame shield mobj
MT_BUBBLEWRAP_ORB, // Bubble shield mobj
MT_THUNDERCOIN_ORB, // Thunder shield mobj
MT_THUNDERCOIN_SPARK, // Thunder spark
MT_IVSP, // Invincibility sparkles
MT_SUPERSPARK, // Super Sonic Spark
// Freed Animals
MT_BIRD, // Birdie freed!
MT_BUNNY, // Bunny freed!
MT_MOUSE, // Mouse
MT_CHICKEN, // Chicken
MT_COW, // Cow
MT_REDBIRD, // Red Birdie in Bubble
// Flickies
MT_FLICKY_01, // Bluebird
MT_FLICKY_02, // Rabbit
MT_FLICKY_03, // Chicken
MT_FLICKY_04, // Seal
MT_FLICKY_05, // Pig
MT_FLICKY_06, // Chipmunk
MT_FLICKY_07, // Penguin
MT_FLICKY_08, // Fish
MT_FLICKY_09, // Ram
MT_FLICKY_10, // Puffin
MT_FLICKY_11, // Cow
MT_FLICKY_12, // Rat
MT_FLICKY_13, // Bear
MT_FLICKY_14, // Dove
MT_FLICKY_15, // Cat
MT_FLICKY_16, // Canary
// Environmental Effects
MT_RAIN, // Rain
@ -3286,6 +3547,7 @@ typedef enum mobj_type
MT_MEDIUMBUBBLE, // medium bubble
MT_EXTRALARGEBUBBLE, // extra large bubble
MT_WATERZAP,
MT_SPINDUST, // Spindash dust
MT_TFOG,
MT_SEED,
MT_PARTICLE,

View file

@ -835,6 +835,16 @@ static int lib_pDoJumpShield(lua_State *L)
return 0;
}
static int lib_pDoBubbleBounce(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
NOHUD
if (!player)
return LUA_ErrInvalid(L, "player_t");
P_DoBubbleBounce(player);
return 0;
}
static int lib_pBlackOw(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
@ -845,13 +855,14 @@ static int lib_pBlackOw(lua_State *L)
return 0;
}
static int lib_pElementalFireTrail(lua_State *L)
static int lib_pElementalFire(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
boolean cropcircle = lua_optboolean(L, 2);
NOHUD
if (!player)
return LUA_ErrInvalid(L, "player_t");
P_ElementalFireTrail(player);
P_ElementalFire(player, cropcircle);
return 0;
}
@ -906,10 +917,11 @@ static int lib_pReturnThrustY(lua_State *L)
static int lib_pLookForEnemies(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
boolean nonenemies = lua_opttrueboolean(L, 2);
NOHUD
if (!player)
return LUA_ErrInvalid(L, "player_t");
lua_pushboolean(L, P_LookForEnemies(player));
lua_pushboolean(L, P_LookForEnemies(player, nonenemies));
return 1;
}
@ -2127,8 +2139,9 @@ static luaL_Reg lib[] = {
{"P_GivePlayerLives",lib_pGivePlayerLives},
{"P_ResetScore",lib_pResetScore},
{"P_DoJumpShield",lib_pDoJumpShield},
{"P_DoBubbleBounce",lib_pDoBubbleBounce},
{"P_BlackOw",lib_pBlackOw},
{"P_ElementalFireTrail",lib_pElementalFireTrail},
{"P_ElementalFire",lib_pElementalFire},
{"P_DoPlayerExit",lib_pDoPlayerExit},
{"P_InstaThrust",lib_pInstaThrust},
{"P_ReturnThrustX",lib_pReturnThrustX},

View file

@ -43,6 +43,8 @@ enum hook {
hook_PlayerMsg,
hook_HurtMsg,
hook_PlayerSpawn,
hook_ShieldSpawn,
hook_ShieldSpecial,
hook_MAX // last hook
};
@ -60,7 +62,7 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which);
#define LUAh_MobjMoveCollide(thing1, thing2) LUAh_MobjCollideHook(thing1, thing2, hook_MobjMoveCollide) // Hook for PIT_CheckThing by (tmthing) mobj type
boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher); // Hook for P_TouchSpecialThing by mobj type
#define LUAh_MobjFuse(mo) LUAh_MobjHook(mo, hook_MobjFuse) // Hook for mobj->fuse == 0 by mobj type
#define LUAh_MobjThinker(mo) LUAh_MobjHook(mo, hook_MobjThinker) // Hook for P_MobjThinker or P_SceneryThinker by mobj type
boolean LUAh_MobjThinker(mobj_t *mo); // Hook for P_MobjThinker or P_SceneryThinker by mobj type
#define LUAh_BossThinker(mo) LUAh_MobjHook(mo, hook_BossThinker) // Hook for P_GenericBossThinker by mobj type
UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); // Hook for P_DamageMobj by mobj type (Should mobj take damage?)
boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); // Hook for P_DamageMobj by mobj type (Mobj actually takes damage!)
@ -77,5 +79,7 @@ boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector); // Hook
boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg); // Hook for chat messages
boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); // Hook for hurt messages
#define LUAh_PlayerSpawn(player) LUAh_PlayerHook(player, hook_PlayerSpawn) // Hook for G_SpawnPlayer
#define LUAh_ShieldSpawn(player) LUAh_PlayerHook(player, hook_ShieldSpawn) // Hook for P_SpawnShieldOrb
#define LUAh_ShieldSpecial(player) LUAh_PlayerHook(player, hook_ShieldSpecial) // Hook for shield abilities
#endif

View file

@ -54,6 +54,8 @@ const char *const hookNames[hook_MAX+1] = {
"PlayerMsg",
"HurtMsg",
"PlayerSpawn",
"ShieldSpawn",
"ShieldSpecial",
NULL
};
@ -74,12 +76,30 @@ typedef struct hook_s* hook_p;
#define FMT_HOOKID "hook_%d"
// For each mobj type, a linked list to its thinker and collision hooks.
// That way, we don't have to iterate through all the hooks.
// We could do that with all other mobj hooks, but it would probably just be
// a waste of memory since they are only called occasionally. Probably...
static hook_p mobjthinkerhooks[NUMMOBJTYPES];
static hook_p mobjcollidehooks[NUMMOBJTYPES];
// For each mobj type, a linked list for other mobj hooks
static hook_p mobjhooks[NUMMOBJTYPES];
// A linked list for player hooks
static hook_p playerhooks;
// A linked list for linedef executor hooks
static hook_p linedefexecutorhooks;
// For other hooks, a unique linked list
hook_p roothook;
// Takes hook, function, and additional arguments (mobj type to act on, etc.)
static int lib_addHook(lua_State *L)
{
static struct hook_s hook = {NULL, 0, 0, {0}, false};
static UINT32 nextid;
hook_p hookp, *lastp;
hook.type = luaL_checkoption(L, 1, NULL, hookNames);
@ -109,6 +129,7 @@ static int lib_addHook(lua_State *L)
hook.s.mt = MT_NULL;
if (lua_isnumber(L, 2))
hook.s.mt = lua_tonumber(L, 2);
luaL_argcheck(L, hook.s.mt < NUMMOBJTYPES, 2, "invalid mobjtype_t");
break;
case hook_BotAI:
hook.s.skinname = NULL;
@ -141,18 +162,49 @@ static int lib_addHook(lua_State *L)
hooksAvailable[hook.type/8] |= 1<<(hook.type%8);
// iterate the hook metadata structs
// set hook.id to the highest id + 1
// set lastp to the last hook struct's "next" pointer.
lastp = &roothook;
hook.id = 0;
for (hookp = roothook; hookp; hookp = hookp->next)
hook.id = nextid++;
// Special cases for some hook types (see the comments above mobjthinkerhooks declaration)
switch(hook.type)
{
if (hookp->id >= hook.id)
hook.id = hookp->id+1;
lastp = &hookp->next;
case hook_MobjThinker:
lastp = &mobjthinkerhooks[hook.s.mt];
break;
case hook_MobjCollide:
case hook_MobjMoveCollide:
lastp = &mobjcollidehooks[hook.s.mt];
break;
case hook_MobjSpawn:
case hook_TouchSpecial:
case hook_MobjFuse:
case hook_BossThinker:
case hook_ShouldDamage:
case hook_MobjDamage:
case hook_MobjDeath:
case hook_BossDeath:
case hook_MobjRemoved:
lastp = &mobjhooks[hook.s.mt];
break;
case hook_JumpSpecial:
case hook_AbilitySpecial:
case hook_SpinSpecial:
case hook_JumpSpinSpecial:
case hook_PlayerSpawn:
lastp = &playerhooks;
break;
case hook_LinedefExecute:
lastp = &linedefexecutorhooks;
break;
default:
lastp = &roothook;
break;
}
// iterate the hook metadata structs
// set lastp to the last hook struct's "next" pointer.
for (hookp = *lastp; hookp; hookp = hookp->next)
lastp = &hookp->next;
// allocate a permanent memory struct to stuff hook.
hookp = ZZ_Alloc(sizeof(struct hook_s));
memcpy(hookp, &hook, sizeof(struct hook_s));
@ -183,9 +235,29 @@ boolean LUAh_MobjHook(mobj_t *mo, enum hook which)
lua_settop(gL, 0);
for (hookp = roothook; hookp; hookp = hookp->next)
if (hookp->type == which
&& (hookp->s.mt == MT_NULL || hookp->s.mt == mo->type))
// Look for all generic mobj hooks
for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
if (hookp->type == which)
{
if (lua_gettop(gL) == 0)
LUA_PushUserdata(gL, mo, META_MOBJ);
lua_pushfstring(gL, FMT_HOOKID, hookp->id);
lua_gettable(gL, LUA_REGISTRYINDEX);
lua_pushvalue(gL, -2);
if (lua_pcall(gL, 1, 1, 0)) {
if (!hookp->error || cv_debug & DBG_LUA)
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
lua_pop(gL, 1);
hookp->error = true;
continue;
}
if (lua_toboolean(gL, -1))
hooked = true;
lua_pop(gL, 1);
}
for (hookp = mobjhooks[mo->type]; hookp; hookp = hookp->next)
if (hookp->type == which)
{
if (lua_gettop(gL) == 0)
LUA_PushUserdata(gL, mo, META_MOBJ);
@ -217,7 +289,7 @@ boolean LUAh_PlayerHook(player_t *plr, enum hook which)
lua_settop(gL, 0);
for (hookp = roothook; hookp; hookp = hookp->next)
for (hookp = playerhooks; hookp; hookp = hookp->next)
if (hookp->type == which)
{
if (lua_gettop(gL) == 0)
@ -338,9 +410,38 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which)
lua_settop(gL, 0);
for (hookp = roothook; hookp; hookp = hookp->next)
if (hookp->type == which
&& (hookp->s.mt == MT_NULL || hookp->s.mt == thing1->type))
// Look for all generic mobj collision hooks
for (hookp = mobjcollidehooks[MT_NULL]; hookp; hookp = hookp->next)
if (hookp->type == which)
{
if (lua_gettop(gL) == 0)
{
LUA_PushUserdata(gL, thing1, META_MOBJ);
LUA_PushUserdata(gL, thing2, META_MOBJ);
}
lua_pushfstring(gL, FMT_HOOKID, hookp->id);
lua_gettable(gL, LUA_REGISTRYINDEX);
lua_pushvalue(gL, -3);
lua_pushvalue(gL, -3);
if (lua_pcall(gL, 2, 1, 0)) {
if (!hookp->error || cv_debug & DBG_LUA)
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
lua_pop(gL, 1);
hookp->error = true;
continue;
}
if (!lua_isnil(gL, -1))
{ // if nil, leave shouldCollide = 0.
if (lua_toboolean(gL, -1))
shouldCollide = 1; // Force yes
else
shouldCollide = 2; // Force no
}
lua_pop(gL, 1);
}
for (hookp = mobjcollidehooks[thing1->type]; hookp; hookp = hookp->next)
if (hookp->type == which)
{
if (lua_gettop(gL) == 0)
{
@ -372,6 +473,59 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which)
return shouldCollide;
}
// Hook for mobj thinkers
boolean LUAh_MobjThinker(mobj_t *mo)
{
hook_p hookp;
boolean hooked = false;
if (!gL || !(hooksAvailable[hook_MobjThinker/8] & (1<<(hook_MobjThinker%8))))
return false;
lua_settop(gL, 0);
// Look for all generic mobj thinker hooks
for (hookp = mobjthinkerhooks[MT_NULL]; hookp; hookp = hookp->next)
{
if (lua_gettop(gL) == 0)
LUA_PushUserdata(gL, mo, META_MOBJ);
lua_pushfstring(gL, FMT_HOOKID, hookp->id);
lua_gettable(gL, LUA_REGISTRYINDEX);
lua_pushvalue(gL, -2);
if (lua_pcall(gL, 1, 1, 0)) {
if (!hookp->error || cv_debug & DBG_LUA)
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
lua_pop(gL, 1);
hookp->error = true;
continue;
}
if (lua_toboolean(gL, -1))
hooked = true;
lua_pop(gL, 1);
}
for (hookp = mobjthinkerhooks[mo->type]; hookp; hookp = hookp->next)
{
if (lua_gettop(gL) == 0)
LUA_PushUserdata(gL, mo, META_MOBJ);
lua_pushfstring(gL, FMT_HOOKID, hookp->id);
lua_gettable(gL, LUA_REGISTRYINDEX);
lua_pushvalue(gL, -2);
if (lua_pcall(gL, 1, 1, 0)) {
if (!hookp->error || cv_debug & DBG_LUA)
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
lua_pop(gL, 1);
hookp->error = true;
continue;
}
if (lua_toboolean(gL, -1))
hooked = true;
lua_pop(gL, 1);
}
lua_settop(gL, 0);
return hooked;
}
// Hook for P_TouchSpecialThing by mobj type
boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher)
{
@ -382,9 +536,33 @@ boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher)
lua_settop(gL, 0);
for (hookp = roothook; hookp; hookp = hookp->next)
if (hookp->type == hook_TouchSpecial
&& (hookp->s.mt == MT_NULL || hookp->s.mt == special->type))
// Look for all generic touch special hooks
for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
if (hookp->type == hook_TouchSpecial)
{
if (lua_gettop(gL) == 0)
{
LUA_PushUserdata(gL, special, META_MOBJ);
LUA_PushUserdata(gL, toucher, META_MOBJ);
}
lua_pushfstring(gL, FMT_HOOKID, hookp->id);
lua_gettable(gL, LUA_REGISTRYINDEX);
lua_pushvalue(gL, -3);
lua_pushvalue(gL, -3);
if (lua_pcall(gL, 2, 1, 0)) {
if (!hookp->error || cv_debug & DBG_LUA)
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
lua_pop(gL, 1);
hookp->error = true;
continue;
}
if (lua_toboolean(gL, -1))
hooked = true;
lua_pop(gL, 1);
}
for (hookp = mobjhooks[special->type]; hookp; hookp = hookp->next)
if (hookp->type == hook_TouchSpecial)
{
if (lua_gettop(gL) == 0)
{
@ -421,9 +599,42 @@ UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
lua_settop(gL, 0);
for (hookp = roothook; hookp; hookp = hookp->next)
if (hookp->type == hook_ShouldDamage
&& (hookp->s.mt == MT_NULL || hookp->s.mt == target->type))
// Look for all generic should damage hooks
for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
if (hookp->type == hook_ShouldDamage)
{
if (lua_gettop(gL) == 0)
{
LUA_PushUserdata(gL, target, META_MOBJ);
LUA_PushUserdata(gL, inflictor, META_MOBJ);
LUA_PushUserdata(gL, source, META_MOBJ);
lua_pushinteger(gL, damage);
}
lua_pushfstring(gL, FMT_HOOKID, hookp->id);
lua_gettable(gL, LUA_REGISTRYINDEX);
lua_pushvalue(gL, -5);
lua_pushvalue(gL, -5);
lua_pushvalue(gL, -5);
lua_pushvalue(gL, -5);
if (lua_pcall(gL, 4, 1, 0)) {
if (!hookp->error || cv_debug & DBG_LUA)
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
lua_pop(gL, 1);
hookp->error = true;
continue;
}
if (!lua_isnil(gL, -1))
{
if (lua_toboolean(gL, -1))
shouldDamage = 1; // Force yes
else
shouldDamage = 2; // Force no
}
lua_pop(gL, 1);
}
for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next)
if (hookp->type == hook_ShouldDamage)
{
if (lua_gettop(gL) == 0)
{
@ -471,9 +682,37 @@ boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
lua_settop(gL, 0);
for (hookp = roothook; hookp; hookp = hookp->next)
if (hookp->type == hook_MobjDamage
&& (hookp->s.mt == MT_NULL || hookp->s.mt == target->type))
// Look for all generic mobj damage hooks
for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
if (hookp->type == hook_MobjDamage)
{
if (lua_gettop(gL) == 0)
{
LUA_PushUserdata(gL, target, META_MOBJ);
LUA_PushUserdata(gL, inflictor, META_MOBJ);
LUA_PushUserdata(gL, source, META_MOBJ);
lua_pushinteger(gL, damage);
}
lua_pushfstring(gL, FMT_HOOKID, hookp->id);
lua_gettable(gL, LUA_REGISTRYINDEX);
lua_pushvalue(gL, -5);
lua_pushvalue(gL, -5);
lua_pushvalue(gL, -5);
lua_pushvalue(gL, -5);
if (lua_pcall(gL, 4, 1, 0)) {
if (!hookp->error || cv_debug & DBG_LUA)
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
lua_pop(gL, 1);
hookp->error = true;
continue;
}
if (lua_toboolean(gL, -1))
hooked = true;
lua_pop(gL, 1);
}
for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next)
if (hookp->type == hook_MobjDamage)
{
if (lua_gettop(gL) == 0)
{
@ -516,9 +755,35 @@ boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8
lua_settop(gL, 0);
for (hookp = roothook; hookp; hookp = hookp->next)
if (hookp->type == hook_MobjDeath
&& (hookp->s.mt == MT_NULL || hookp->s.mt == target->type))
// Look for all generic mobj death hooks
for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
if (hookp->type == hook_MobjDeath)
{
if (lua_gettop(gL) == 0)
{
LUA_PushUserdata(gL, target, META_MOBJ);
LUA_PushUserdata(gL, inflictor, META_MOBJ);
LUA_PushUserdata(gL, source, META_MOBJ);
}
lua_pushfstring(gL, FMT_HOOKID, hookp->id);
lua_gettable(gL, LUA_REGISTRYINDEX);
lua_pushvalue(gL, -4);
lua_pushvalue(gL, -4);
lua_pushvalue(gL, -4);
if (lua_pcall(gL, 3, 1, 0)) {
if (!hookp->error || cv_debug & DBG_LUA)
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
lua_pop(gL, 1);
hookp->error = true;
continue;
}
if (lua_toboolean(gL, -1))
hooked = true;
lua_pop(gL, 1);
}
for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next)
if (hookp->type == hook_MobjDeath)
{
if (lua_gettop(gL) == 0)
{
@ -658,9 +923,8 @@ boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector)
lua_settop(gL, 0);
for (hookp = roothook; hookp; hookp = hookp->next)
if (hookp->type == hook_LinedefExecute
&& !strcmp(hookp->s.funcname, line->text))
for (hookp = linedefexecutorhooks; hookp; hookp = hookp->next)
if (!strcmp(hookp->s.funcname, line->text))
{
if (lua_gettop(gL) == 0)
{

View file

@ -424,22 +424,12 @@ static int sector_get(lua_State *L)
case sector_ceilingheight:
lua_pushfixed(L, sector->ceilingheight);
return 1;
case sector_floorpic: { // floorpic
levelflat_t *levelflat;
INT16 i;
for (i = 0, levelflat = levelflats; i != sector->floorpic; i++, levelflat++)
;
lua_pushlstring(L, levelflat->name, 8);
case sector_floorpic: // floorpic
lua_pushlstring(L, levelflats[sector->floorpic].name, 8);
return 1;
}
case sector_ceilingpic: { // ceilingpic
levelflat_t *levelflat;
INT16 i;
for (i = 0, levelflat = levelflats; i != sector->ceilingpic; i++, levelflat++)
;
lua_pushlstring(L, levelflat->name, 8);
case sector_ceilingpic: // ceilingpic
lua_pushlstring(L, levelflats[sector->ceilingpic].name, 8);
return 1;
}
case sector_lightlevel:
lua_pushinteger(L, sector->lightlevel);
return 1;
@ -476,46 +466,6 @@ static int sector_get(lua_State *L)
return 0;
}
// help function for P_LoadSectors, find a flat in the active wad files,
// allocate an id for it, and set the levelflat (to speedup search)
//
static INT32 P_AddLevelFlatRuntime(const char *flatname)
{
size_t i;
levelflat_t *levelflat = levelflats;
//
// first scan through the already found flats
//
for (i = 0; i < numlevelflats; i++, levelflat++)
if (strnicmp(levelflat->name,flatname,8)==0)
break;
// that flat was already found in the level, return the id
if (i == numlevelflats)
{
// allocate new flat memory
levelflats = Z_Realloc(levelflats, (numlevelflats + 1) * sizeof(*levelflats), PU_LEVEL, NULL);
levelflat = levelflats+i;
// store the name
strlcpy(levelflat->name, flatname, sizeof (levelflat->name));
strupr(levelflat->name);
// store the flat lump number
levelflat->lumpnum = R_GetFlatNumForName(flatname);
#ifndef ZDEBUG
CONS_Debug(DBG_SETUP, "flat #%03d: %s\n", atoi(sizeu1(numlevelflats)), levelflat->name);
#endif
numlevelflats++;
}
// level flat id
return (INT32)i;
}
static int sector_set(lua_State *L)
{
sector_t *sector = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR));

View file

@ -28,4 +28,4 @@ void M_AATreeSet(aatree_t *aatree, INT32 key, void* value);
void *M_AATreeGet(aatree_t *aatree, INT32 key);
void M_AATreeIterate(aatree_t *aatree, aatree_iter_t callback);
#endif
#endif

View file

@ -46,41 +46,6 @@ typedef INT32 fixed_t;
#define FLOAT_TO_FIXED(f) (fixed_t)((f) * ((float)FRACUNIT))
/** \brief The TMulScale16 function
\param a a parameter of type fixed_t
\param b a parameter of type fixed_t
\param c a parameter of type fixed_t
\param d a parameter of type fixed_t
\param e a parameter of type fixed_t
\param f a parameter of type fixed_t
\return fixed_t
*/
FUNCMATH FUNCINLINE static ATTRINLINE fixed_t TMulScale16(fixed_t a, fixed_t b, fixed_t c, fixed_t d, fixed_t e, fixed_t f) \
{ \
return (fixed_t)((((INT64)a * (INT64)b) + ((INT64)c * (INT64)d) \
+ ((INT64)e * (INT64)f)) >> 16); \
}
/** \brief The DMulScale16 function
\param a a parameter of type fixed_t
\param b a parameter of type fixed_t
\param c a parameter of type fixed_t
\param d a parameter of type fixed_t
\return fixed_t
*/
FUNCMATH FUNCINLINE static ATTRINLINE fixed_t DMulScale16(fixed_t a, fixed_t b, fixed_t c, fixed_t d) \
{ \
return (fixed_t)((((INT64)a * (INT64)b) + ((INT64)c * (INT64)d)) >> 16); \
}
#if defined (__WATCOMC__) && FRACBITS == 16
#pragma aux FixedMul = \
"imul ebx", \
@ -283,9 +248,16 @@ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedFloor(fixed_t x)
{
const fixed_t a = abs(x); //absolute of x
const fixed_t i = (a>>FRACBITS)<<FRACBITS; // cut out the fractional part
const fixed_t f = i-a; // cut out the integral part
const fixed_t f = a-i; // cut out the integral part
if (f == 0)
return x;
if (x != INT32_MIN)
return x-f; // return largest integral value not greater than argument
{ // return rounded down to nearest whole number
if (x > 0)
return x-f;
else
return x-(FRACUNIT-f);
}
return INT32_MIN;
}
@ -301,7 +273,7 @@ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedTrunc(fixed_t x)
{
const fixed_t a = abs(x); //absolute of x
const fixed_t i = (a>>FRACBITS)<<FRACBITS; // cut out the fractional part
const fixed_t f = i-a; // cut out the integral part
const fixed_t f = a-i; // cut out the integral part
if (x != INT32_MIN)
{ // return rounded to nearest whole number, towards zero
if (x > 0)
@ -324,11 +296,18 @@ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedCeil(fixed_t x)
{
const fixed_t a = abs(x); //absolute of x
const fixed_t i = (a>>FRACBITS)<<FRACBITS; // cut out the fractional part
const fixed_t f = i-a; // cut out the integral part
const fixed_t f = a-i; // cut out the integral part
if (f == 0)
return x;
if (x == INT32_MIN)
return INT32_MIN;
else if (x < FixedFloor(INT32_MAX))
return x+(FRACUNIT-f); // return smallest integral value not less than argument
{ // return rounded up to nearest whole number
if (x > 0)
return x+(FRACUNIT-f);
else
return x+f;
}
return INT32_MAX;
}
@ -344,7 +323,9 @@ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedRound(fixed_t x)
{
const fixed_t a = abs(x); //absolute of x
const fixed_t i = (a>>FRACBITS)<<FRACBITS; // cut out the fractional part
const fixed_t f = i-a; // cut out the integral part
const fixed_t f = a-i; // cut out the integral part
if (f == 0)
return x;
if (x == INT32_MIN)
return INT32_MIN;
else if (x < FixedFloor(INT32_MAX))

View file

@ -1617,6 +1617,11 @@ INT32 axtoi(const char *hexStg)
return intValue;
}
// Token parser variables
static UINT32 oldendPos = 0; // old value of endPos, used by M_UnGetToken
static UINT32 endPos = 0; // now external to M_GetToken, but still static
/** Token parser for TEXTURES, ANIMDEFS, and potentially other lumps later down the line.
* Was originally R_GetTexturesToken when I was coding up the TEXTURES parser, until I realized I needed it for ANIMDEFS too.
* Parses up to the next whitespace character or comma. When finding the start of the next token, whitespace is skipped.
@ -1631,7 +1636,7 @@ char *M_GetToken(const char *inputString)
{
static const char *stringToUse = NULL; // Populated if inputString != NULL; used otherwise
static UINT32 startPos = 0;
static UINT32 endPos = 0;
// static UINT32 endPos = 0;
static UINT32 stringLength = 0;
static UINT8 inComment = 0; // 0 = not in comment, 1 = // Single-line, 2 = /* Multi-line */
char *texturesToken = NULL;
@ -1641,12 +1646,12 @@ char *M_GetToken(const char *inputString)
{
stringToUse = inputString;
startPos = 0;
endPos = 0;
oldendPos = endPos = 0;
stringLength = strlen(inputString);
}
else
{
startPos = endPos;
startPos = oldendPos = endPos;
}
if (stringToUse == NULL)
return NULL;
@ -1777,6 +1782,16 @@ char *M_GetToken(const char *inputString)
return texturesToken;
}
/** Undoes the last M_GetToken call
* The current position along the string being parsed is reset to the last saved position.
* This exists mostly because of R_ParseTexture/R_ParsePatch honestly, but could be useful elsewhere?
* -Monster Iestyn (22/10/16)
*/
void M_UnGetToken(void)
{
endPos = oldendPos;
}
/** Count bits in a number.
*/
UINT8 M_CountBits(UINT32 num, UINT8 size)

View file

@ -104,6 +104,9 @@ void A_BombShield(mobj_t *actor);
void A_WaterShield(mobj_t *actor);
void A_ForceShield(mobj_t *actor);
void A_PityShield(mobj_t *actor);
void A_FlameShield(mobj_t *actor);
void A_BubbleShield(mobj_t *actor);
void A_ThunderShield(mobj_t *actor);
void A_GravityBox(mobj_t *actor);
void A_ScoreRise(mobj_t *actor);
void A_ParticleSpawn(mobj_t *actor);
@ -239,6 +242,16 @@ void A_BrakFireShot(mobj_t *actor);
void A_BrakLobShot(mobj_t *actor);
void A_NapalmScatter(mobj_t *actor);
void A_SpawnFreshCopy(mobj_t *actor);
void A_FlickySpawn(mobj_t *actor);
void A_FlickyAim(mobj_t *actor);
void A_FlickyFly(mobj_t *actor);
void A_FlickySoar(mobj_t *actor);
void A_FlickyCoast(mobj_t *actor);
void A_FlickyHop(mobj_t *actor);
void A_FlickyFlounder(mobj_t *actor);
void A_FlickyCheck(mobj_t *actor);
void A_FlickyHeightCheck(mobj_t *actor);
void A_FlickyFlutter(mobj_t *actor);
//
// ENEMY THINKING
@ -738,7 +751,7 @@ static boolean P_LookForShield(mobj_t *actor)
(actor->type == MT_BLUETEAMRING && player->ctfteam != 2))
continue;
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT
if ((player->powers[pw_shield] & SH_PROTECTELECTRIC)
&& (P_AproxDistance(P_AproxDistance(actor->x-player->mo->x, actor->y-player->mo->y), actor->z-player->mo->z) < FixedMul(RING_DIST, player->mo->scale)))
{
P_SetTarget(&actor->tracer, player->mo);
@ -3059,11 +3072,7 @@ void A_JumpShield(mobj_t *actor)
player = actor->target->player;
if ((player->powers[pw_shield] & SH_NOSTACK) != SH_JUMP)
{
player->powers[pw_shield] = SH_JUMP|(player->powers[pw_shield] & SH_STACK);
P_SpawnShieldOrb(player);
}
P_SwitchShield(player, SH_WHIRLWIND);
S_StartSound(player->mo, actor->info->seesound);
}
@ -3091,11 +3100,7 @@ void A_RingShield(mobj_t *actor)
player = actor->target->player;
if ((player->powers[pw_shield] & SH_NOSTACK) != SH_ATTRACT)
{
player->powers[pw_shield] = SH_ATTRACT|(player->powers[pw_shield] & SH_STACK);
P_SpawnShieldOrb(player);
}
P_SwitchShield(player, SH_ATTRACT);
S_StartSound(player->mo, actor->info->seesound);
}
@ -3292,11 +3297,12 @@ void A_BombShield(mobj_t *actor)
player = actor->target->player;
if ((player->powers[pw_shield] & SH_NOSTACK) != SH_BOMB)
{
player->powers[pw_shield] = SH_BOMB|(player->powers[pw_shield] & SH_STACK);
P_SpawnShieldOrb(player);
}
// If you already have a bomb shield, use it!
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ARMAGEDDON)
P_BlackOw(player);
// Now we know for certain that we don't have a bomb shield, so add one. :3
P_SwitchShield(player, SH_ARMAGEDDON);
S_StartSound(player->mo, actor->info->seesound);
}
@ -3324,22 +3330,8 @@ void A_WaterShield(mobj_t *actor)
player = actor->target->player;
if ((player->powers[pw_shield] & SH_NOSTACK) != SH_ELEMENTAL)
{
player->powers[pw_shield] = SH_ELEMENTAL|(player->powers[pw_shield] & SH_STACK);
P_SpawnShieldOrb(player);
}
P_SwitchShield(player, SH_ELEMENTAL);
if (player->powers[pw_underwater] && player->powers[pw_underwater] <= 12*TICRATE + 1)
P_RestoreMusic(player);
player->powers[pw_underwater] = 0;
if (player->powers[pw_spacetime] > 1)
{
player->powers[pw_spacetime] = 0;
P_RestoreMusic(player);
}
S_StartSound(player->mo, actor->info->seesound);
}
@ -3347,12 +3339,13 @@ void A_WaterShield(mobj_t *actor)
//
// Description: Awards the player a force shield.
//
// var1 = unused
// var1 = Number of additional hitpoints to give
// var2 = unused
//
void A_ForceShield(mobj_t *actor)
{
player_t *player;
INT32 locvar1 = var1;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_ForceShield", actor))
@ -3364,15 +3357,15 @@ void A_ForceShield(mobj_t *actor)
return;
}
if (locvar1 & ~SH_FORCEHP)
{
CONS_Debug(DBG_GAMELOGIC, "Invalid number of additional hitpoints.\n");
return;
}
player = actor->target->player;
if (!(player->powers[pw_shield] & SH_FORCE))
{
player->powers[pw_shield] = SH_FORCE|(player->powers[pw_shield] & SH_STACK)|0x01;
P_SpawnShieldOrb(player);
}
else
player->powers[pw_shield] = SH_FORCE|(player->powers[pw_shield] & SH_STACK)|0x01;
P_SwitchShield(player, SH_FORCE|locvar1);
S_StartSound(player->mo, actor->info->seesound);
}
@ -3404,12 +3397,92 @@ void A_PityShield(mobj_t *actor)
player = actor->target->player;
if ((player->powers[pw_shield] & SH_NOSTACK) != SH_PITY)
P_SwitchShield(player, SH_PITY);
S_StartSound(player->mo, actor->info->seesound);
}
// Function: A_FlameShield
//
// Description: Awards the player a flame shield.
//
// var1 = unused
// var2 = unused
//
void A_FlameShield(mobj_t *actor)
{
player_t *player;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FlameShield", actor))
return;
#endif
if (!actor->target || !actor->target->player)
{
player->powers[pw_shield] = SH_PITY+(player->powers[pw_shield] & SH_STACK);
P_SpawnShieldOrb(player);
CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
return;
}
player = actor->target->player;
P_SwitchShield(player, SH_FLAMEAURA);
S_StartSound(player->mo, actor->info->seesound);
}
// Function: A_BubbleShield
//
// Description: Awards the player a bubble shield.
//
// var1 = unused
// var2 = unused
//
void A_BubbleShield(mobj_t *actor)
{
player_t *player;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_BubbleShield", actor))
return;
#endif
if (!actor->target || !actor->target->player)
{
CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
return;
}
player = actor->target->player;
P_SwitchShield(player, SH_BUBBLEWRAP);
S_StartSound(player->mo, actor->info->seesound);
}
// Function: A_ThunderShield
//
// Description: Awards the player a thunder shield.
//
// var1 = unused
// var2 = unused
//
void A_ThunderShield(mobj_t *actor)
{
player_t *player;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_ThunderShield", actor))
return;
#endif
if (!actor->target || !actor->target->player)
{
CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
return;
}
player = actor->target->player;
P_SwitchShield(player, SH_THUNDERCOIN);
S_StartSound(player->mo, actor->info->seesound);
}
@ -3436,9 +3509,10 @@ void A_GravityBox(mobj_t *actor)
}
player = actor->target->player;
player->powers[pw_gravityboots] = (UINT16)(actor->info->reactiontime + 1);
S_StartSound(player, actor->info->activesound);
player->powers[pw_gravityboots] = (UINT16)(actor->info->reactiontime + 1);
}
// Function: A_ScoreRise
@ -3704,7 +3778,7 @@ void A_AttractChase(mobj_t *actor)
// Turn flingrings back into regular rings if attracted.
if (actor->tracer && actor->tracer->player
&& (actor->tracer->player->powers[pw_shield] & SH_NOSTACK) != SH_ATTRACT && actor->info->reactiontime && actor->type != (mobjtype_t)actor->info->reactiontime)
&& !(actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC) && actor->info->reactiontime && actor->type != (mobjtype_t)actor->info->reactiontime)
{
mobj_t *newring;
newring = P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->reactiontime);
@ -3909,7 +3983,7 @@ void A_ThrownRing(mobj_t *actor)
// A non-homing ring getting attracted by a
// magnetic player. If he gets too far away, make
// sure to stop the attraction!
if ((!actor->tracer->health) || (actor->tracer->player && (actor->tracer->player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT
if ((!actor->tracer->health) || (actor->tracer->player && (actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC)
&& P_AproxDistance(P_AproxDistance(actor->tracer->x-actor->x,
actor->tracer->y-actor->y), actor->tracer->z-actor->z) > FixedMul(RING_DIST/4, actor->tracer->scale)))
{
@ -3917,7 +3991,7 @@ void A_ThrownRing(mobj_t *actor)
}
if (actor->tracer && (actor->tracer->health)
&& (actor->tracer->player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT)// Already found someone to follow.
&& (actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC))// Already found someone to follow.
{
const INT32 temp = actor->threshold;
actor->threshold = 32000;
@ -3985,7 +4059,7 @@ void A_ThrownRing(mobj_t *actor)
if (!P_CheckSight(actor, player->mo))
continue; // out of sight
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT
if ((player->powers[pw_shield] & SH_PROTECTELECTRIC)
&& dist < FixedMul(RING_DIST/4, player->mo->scale))
P_SetTarget(&actor->tracer, player->mo);
return;
@ -7764,7 +7838,7 @@ void A_SetObjectFlags(mobj_t *actor)
else if (locvar2 == 1)
locvar1 = actor->flags & ~locvar1;
if ((locvar1 & (MF_NOBLOCKMAP|MF_NOSECTOR)) != (actor->flags & (MF_NOBLOCKMAP|MF_NOSECTOR))) // Blockmap/sector status has changed, so reset the links
if ((UINT32)(locvar1 & (MF_NOBLOCKMAP|MF_NOSECTOR)) != (actor->flags & (MF_NOBLOCKMAP|MF_NOSECTOR))) // Blockmap/sector status has changed, so reset the links
unlinkthings = true;
if (unlinkthings) {
@ -10272,3 +10346,392 @@ void A_SpawnFreshCopy(mobj_t *actor)
if (newObject->info->seesound)
S_StartSound(newObject, newObject->info->seesound);
}
// Internal Flicky spawning function.
mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz, boolean lookforplayers)
{
mobj_t *flicky;
if (!flickytype)
{
if (!mapheaderinfo[gamemap-1] || !mapheaderinfo[gamemap-1]->numFlickies) // No mapheader, no shoes, no service.
return NULL;
else
{
INT32 prandom = P_RandomKey(mapheaderinfo[gamemap-1]->numFlickies);
flickytype = mapheaderinfo[gamemap-1]->flickies[prandom];
}
}
flicky = P_SpawnMobjFromMobj(actor, 0, 0, 0, flickytype);
flicky->angle = actor->angle;
if (flickytype == MT_SEED)
flicky->z += P_MobjFlip(actor)*(actor->height - flicky->height)/2;
if (actor->eflags & MFE_UNDERWATER)
momz = FixedDiv(momz, FixedSqrt(3*FRACUNIT));
P_SetObjectMomZ(flicky, momz, false);
flicky->movedir = (P_RandomChance(FRACUNIT/2) ? -1 : 1);
flicky->fuse = P_RandomRange(595, 700); // originally 300, 350
flicky->threshold = 0;
if (lookforplayers)
P_LookForPlayers(flicky, true, false, 0);
return flicky;
}
// Function: A_FlickySpawn
//
// Description: Flicky spawning function.
//
// var1:
// lower 16 bits: if 0, spawns random flicky based on level header. Else, spawns the designated thing type.
// upper 16 bits: if 0, no sound is played. Else, A_Scream is called.
// var2 = upwards thrust for spawned flicky. If zero, default value is provided.
//
void A_FlickySpawn(mobj_t *actor)
{
INT32 locvar1 = var1;
INT32 locvar2 = var2;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FlickySpawn", actor))
return;
#endif
if (locvar1 >> 16) {
A_Scream(actor); // A shortcut for the truly lazy.
locvar1 &= 65535;
}
P_InternalFlickySpawn(actor, locvar1, ((locvar2) ? locvar2 : 8*FRACUNIT), true);
}
// Internal Flicky bubbling function.
void P_InternalFlickyBubble(mobj_t *actor)
{
if (actor->eflags & MFE_UNDERWATER)
{
mobj_t *overlay;
if (!((actor->z + 3*actor->height/2) < actor->watertop) || !mobjinfo[actor->type].raisestate || actor->tracer)
return;
overlay = P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY);
P_SetMobjStateNF(overlay, mobjinfo[actor->type].raisestate);
P_SetTarget(&actor->tracer, overlay);
P_SetTarget(&overlay->target, actor);
return;
}
if (!actor->tracer || P_MobjWasRemoved(actor->tracer))
return;
P_RemoveMobj(actor->tracer);
P_SetTarget(&actor->tracer, NULL);
}
// Function: A_FlickyAim
//
// Description: Flicky aiming function.
//
// var1 = how far around the target (in angle constants) the flicky should look
// var2 = distance from target to aim for
//
void A_FlickyAim(mobj_t *actor)
{
INT32 locvar1 = var1;
INT32 locvar2 = var2;
boolean flickyhitwall = false;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FlickyAim", actor))
return;
#endif
if (actor->momx == actor->momy && actor->momy == 0)
flickyhitwall = true;
P_InternalFlickyBubble(actor);
P_InstaThrust(actor, 0, 0);
if (!actor->target)
{
P_LookForPlayers(actor, true, false, 0);
actor->angle = P_RandomKey(36)*ANG10;
return;
}
if (actor->fuse > 2*TICRATE)
{
angle_t posvar;
fixed_t chasevar, chasex, chasey;
if (flickyhitwall)
actor->movedir *= -1;
posvar = ((R_PointToAngle2(actor->target->x, actor->target->y, actor->x, actor->y) + actor->movedir*locvar1) >> ANGLETOFINESHIFT) & FINEMASK;
chasevar = FixedSqrt(max(FRACUNIT, P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y) - locvar2)) + locvar2;
chasex = actor->target->x + FixedMul(FINECOSINE(posvar), chasevar);
chasey = actor->target->y + FixedMul(FINESINE(posvar), chasevar);
if (P_AproxDistance(chasex - actor->x, chasey - actor->y))
actor->angle = R_PointToAngle2(actor->x, actor->y, chasex, chasey);
}
else if (flickyhitwall)
{
actor->angle += ANGLE_180;
actor->threshold = 0;
}
}
//Internal Flicky flying function. Also usuable as an underwater swim thrust.
void P_InternalFlickyFly(mobj_t *actor, fixed_t flyspeed, fixed_t targetdist, fixed_t chasez)
{
angle_t vertangle;
flyspeed = FixedMul(flyspeed, actor->scale);
actor->flags |= MF_NOGRAVITY;
var1 = ANG30;
var2 = 32*FRACUNIT;
A_FlickyAim(actor);
chasez *= 8;
if (!actor->target || !(actor->fuse > 2*TICRATE))
chasez += ((actor->eflags & MFE_VERTICALFLIP) ? actor->ceilingz - 24*FRACUNIT : actor->floorz + 24*FRACUNIT);
else
{
fixed_t add = actor->target->z + (actor->target->height - actor->height)/2;
if (add > (actor->ceilingz - 24*actor->scale - actor->height))
add = actor->ceilingz - 24*actor->scale - actor->height;
else if (add < (actor->floorz + 24*actor->scale))
add = actor->floorz + 24*actor->scale;
chasez += add;
}
if (!targetdist)
targetdist = 16*FRACUNIT; //Default!
if (actor->target && abs(chasez - actor->z) > targetdist)
targetdist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
vertangle = (R_PointToAngle2(0, actor->z, targetdist, chasez) >> ANGLETOFINESHIFT) & FINEMASK;
P_InstaThrust(actor, actor->angle, FixedMul(FINECOSINE(vertangle), flyspeed));
actor->momz = FixedMul(FINESINE(vertangle), flyspeed);
}
// Function: A_FlickyFly
//
// Description: Flicky flying function.
//
// var1 = how fast to fly
// var2 = how far ahead the target should be considered
//
void A_FlickyFly(mobj_t *actor)
{
// We're not setting up locvars here - it passes var1 and var2 through to P_InternalFlickyFly instead.
//INT32 locvar1 = var1;
//INT32 locvar2 = var2;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FlickyFly", actor))
return;
#endif
P_InternalFlickyFly(actor, var1, var2,
FINECOSINE((((actor->fuse % 36) * ANG10) >> ANGLETOFINESHIFT) & FINEMASK)
);
}
// Function: A_FlickySoar
//
// Description: Flicky soaring function - specific to puffin.
//
// var1 = how fast to fly
// var2 = how far ahead the target should be considered
//
void A_FlickySoar(mobj_t *actor)
{
// We're not setting up locvars here - it passes var1 and var2 through to P_InternalFlickyFly instead.
//INT32 locvar1 = var1;
//INT32 locvar2 = var2;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FlickySoar", actor))
return;
#endif
P_InternalFlickyFly(actor, var1, var2,
2*(FRACUNIT/2 - abs(FINECOSINE((((actor->fuse % 144) * 5*ANG1/2) >> ANGLETOFINESHIFT) & FINEMASK)))
);
if (P_MobjFlip(actor)*actor->momz > 0 && actor->frame == 1 && actor->sprite == SPR_FL10)
actor->frame = 3;
}
//Function: A_FlickyCoast
//
// Description: Flicky swim-coasting function.
//
// var1 = speed to change state upon reaching
// var2 = state to change to upon slowing down
// the spawnstate of the mobj = state to change to when above water
//
void A_FlickyCoast(mobj_t *actor)
{
INT32 locvar1 = var1;
INT32 locvar2 = var2;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FlickyCoast", actor))
return;
#endif
if (actor->eflags & MFE_UNDERWATER)
{
actor->momx = (11*actor->momx)/12;
actor->momy = (11*actor->momy)/12;
actor->momz = (11*actor->momz)/12;
if (P_AproxDistance(P_AproxDistance(actor->momx, actor->momy), actor->momz) < locvar1)
P_SetMobjState(actor, locvar2);
return;
}
actor->flags &= ~MF_NOGRAVITY;
P_SetMobjState(actor, mobjinfo[actor->type].spawnstate);
}
// Internal Flicky hopping function.
void P_InternalFlickyHop(mobj_t *actor, fixed_t momz, fixed_t momh, angle_t angle)
{
if (((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
|| ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)))
{
if (momz)
{
if (actor->eflags & MFE_UNDERWATER)
momz = FixedDiv(momz, FixedSqrt(3*FRACUNIT));
P_SetObjectMomZ(actor, momz, false);
}
P_InstaThrust(actor, angle, FixedMul(momh, actor->scale));
}
}
// Function: A_FlickyHop
//
// Description: Flicky hopping function.
//
// var1 = vertical thrust
// var2 = horizontal thrust
//
void A_FlickyHop(mobj_t *actor)
{
// We're not setting up locvars here - it passes var1 and var2 through to P_InternalFlickyHop instead.
//INT32 locvar1 = var1;
//INT32 locvar2 = var2;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FlickyHop", actor))
return;
#endif
P_InternalFlickyHop(actor, var1, var2, actor->angle);
}
// Function: A_FlickyFlounder
//
// Description: Flicky floundering function.
//
// var1 = intended vertical thrust
// var2 = intended horizontal thrust
//
void A_FlickyFlounder(mobj_t *actor)
{
INT32 locvar1 = var1;
INT32 locvar2 = var2;
angle_t hopangle;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FlickyFlounder", actor))
return;
#endif
locvar1 *= (P_RandomKey(2) + 1);
locvar2 *= (P_RandomKey(2) + 1);
hopangle = (actor->angle + (P_RandomKey(9) - 4)*ANG2);
P_InternalFlickyHop(actor, locvar1, locvar2, hopangle);
}
// Function: A_FlickyCheck
//
// Description: Flicky airtime check function.
//
// var1 = state to change to upon touching the floor
// var2 = state to change to upon falling
// the meleestate of the mobj = state to change to when underwater
//
void A_FlickyCheck(mobj_t *actor)
{
INT32 locvar1 = var1;
INT32 locvar2 = var2;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FlickyCheck", actor))
return;
#endif
if (locvar2 && P_MobjFlip(actor)*actor->momz < 1)
P_SetMobjState(actor, locvar2);
else if (locvar1 && ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
|| ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)))
P_SetMobjState(actor, locvar1);
else if (mobjinfo[actor->type].meleestate && (actor->eflags & MFE_UNDERWATER))
P_SetMobjState(actor, mobjinfo[actor->type].meleestate);
P_InternalFlickyBubble(actor);
}
// Function: A_FlickyHeightCheck
//
// Description: Flicky height check function.
//
// var1 = state to change to when falling below height relative to target
// var2 = height relative to target to change state at
//
void A_FlickyHeightCheck(mobj_t *actor)
{
INT32 locvar1 = var1;
INT32 locvar2 = var2;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FlickyHeightCheck", actor))
return;
#endif
if (locvar1 && actor->target && P_MobjFlip(actor)*actor->momz < 1
&& ((P_MobjFlip(actor)*((actor->z + actor->height/2) - (actor->target->z + actor->target->height/2)) < locvar2)
|| (actor->z - actor->height < actor->floorz) || (actor->z + 2*actor->height > actor->ceilingz)))
P_SetMobjState(actor, locvar1);
P_InternalFlickyBubble(actor);
}
// Function: A_FlickyFlutter
//
// Description: Flicky fluttering function - specific to chicken.
//
// var1 = state to change to upon touching the floor
// var2 = state to change to upon falling
// the meleestate of the mobj = state to change to when underwater
//
void A_FlickyFlutter(mobj_t *actor)
{
// We're not setting up locvars here - it passes var1 and var2 through to A_FlickyCheck instead.
//INT32 locvar1 = var1;
//INT32 locvar2 = var2;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FlickyFlutter", actor))
return;
#endif
A_FlickyCheck(actor);
var1 = ANG30;
var2 = 32*FRACUNIT;
A_FlickyAim(actor);
P_InstaThrust(actor, actor->angle, 2*actor->scale);
if (P_MobjFlip(actor)*actor->momz < -FRACUNIT/2)
actor->momz = -P_MobjFlip(actor)*actor->scale/2;
}
#undef FLICKYHITWALL

View file

@ -1748,12 +1748,15 @@ static mobj_t *SearchMarioNode(msecnode_t *node)
case MT_GHOST:
case MT_OVERLAY:
case MT_EMERALDSPAWN:
case MT_GREENORB:
case MT_YELLOWORB:
case MT_BLUEORB:
case MT_BLACKORB:
case MT_WHITEORB:
case MT_PITYORB:
case MT_ELEMENTAL_ORB:
case MT_ATTRACT_ORB:
case MT_FORCE_ORB:
case MT_ARMAGEDDON_ORB:
case MT_WHIRLWIND_ORB:
case MT_PITY_ORB:
case MT_FLAMEAURA_ORB:
case MT_BUBBLEWRAP_ORB:
case MT_THUNDERCOIN_ORB:
case MT_IVSP:
case MT_SUPERSPARK:
case MT_RAIN:
@ -1782,8 +1785,8 @@ static mobj_t *SearchMarioNode(msecnode_t *node)
break;
}
// Ignore popped monitors, too.
if (node->m_thing->flags & MF_MONITOR
&& node->m_thing->threshold == 68)
if (node->m_thing->health == 0 // this only really applies for monitors
|| (!(node->m_thing->flags & MF_MONITOR) && (mobjinfo[node->m_thing->type].flags & MF_MONITOR))) // gold monitor support
continue;
// Okay, we found something valid.
if (!thing // take either the first thing
@ -3156,15 +3159,15 @@ INT32 EV_MarioBlock(sector_t *sec, sector_t *roversector, fixed_t topheight, mob
S_StartSound(puncher, sfx_mario9); // Puncher is "close enough"
}
if (itsamonitor)
if (itsamonitor && thing)
{
P_UnsetThingPosition(tmthing);
tmthing->x = oldx;
tmthing->y = oldy;
tmthing->z = oldz;
tmthing->momx = 1;
tmthing->momy = 1;
P_SetThingPosition(tmthing);
P_UnsetThingPosition(thing);
thing->x = oldx;
thing->y = oldy;
thing->z = oldz;
thing->momx = 1;
thing->momy = 1;
P_SetThingPosition(thing);
}
}
else

View file

@ -142,7 +142,10 @@ boolean P_CanPickupItem(player_t *player, boolean weapon)
if (player->bot && weapon)
return false;
if (player->powers[pw_flashing] > (flashingtics/4)*3 && player->powers[pw_flashing] <= flashingtics)
if (player->powers[pw_flashing] > (flashingtics/4)*3 && player->powers[pw_flashing] < UINT16_MAX)
return false;
if (player->mo && player->mo->health <= 0)
return false;
return true;
@ -296,6 +299,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
{
player_t *player;
INT32 i;
UINT8 elementalpierce;
if (objectplacing)
return;
@ -350,6 +354,11 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
return;
#endif
// 0 = none, 1 = elemental pierce, 2 = bubble bounce
elementalpierce = (((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL || (player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP) && (player->pflags & PF_SHIELDABILITY)
? (((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) ? 1 : 2)
: 0);
if (special->flags & MF_BOSS)
{
if (special->type == MT_BLACKEGGMAN)
@ -359,14 +368,20 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
}
if (((player->pflags & PF_NIGHTSMODE) && (player->pflags & PF_DRILLING))
|| ((player->pflags & PF_JUMPED) && !(player->charflags & SF_NOJUMPDAMAGE && !(player->charability == CA_TWINSPIN && player->panim == PA_ABILITY)))
|| ((player->pflags & PF_JUMPED) && (player->pflags & PF_FORCEJUMPDAMAGE || !(player->charflags & SF_NOJUMPSPIN) || (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY)))
|| (player->pflags & (PF_SPINNING|PF_GLIDING))
|| (player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2)
|| ((player->charflags & SF_STOMPDAMAGE) && (P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0) && (P_MobjFlip(toucher)*toucher->momz < 0))
|| player->powers[pw_invulnerability] || player->powers[pw_super]) // Do you possess the ability to subdue the object?
|| player->powers[pw_invulnerability] || player->powers[pw_super]
|| elementalpierce) // Do you possess the ability to subdue the object?
{
if (P_MobjFlip(toucher)*toucher->momz < 0)
toucher->momz = -toucher->momz;
if ((P_MobjFlip(toucher)*toucher->momz < 0) && (elementalpierce != 1))
{
if (elementalpierce == 2)
P_DoBubbleBounce(player);
else
toucher->momz = -toucher->momz;
}
toucher->momx = -toucher->momx;
toucher->momy = -toucher->momy;
P_DamageMobj(special, toucher, toucher, 1, 0);
@ -392,7 +407,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
/////ENEMIES!!//////////////////////////////////////////
////////////////////////////////////////////////////////
if (special->type == MT_GSNAPPER && !(((player->pflags & PF_NIGHTSMODE) && (player->pflags & PF_DRILLING))
|| player->powers[pw_invulnerability] || player->powers[pw_super])
|| player->powers[pw_invulnerability] || player->powers[pw_super] || elementalpierce)
&& toucher->z < special->z + special->height && toucher->z + toucher->height > special->z)
{
// Can only hit snapper from above
@ -405,14 +420,19 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
P_DamageMobj(toucher, special, special, 1, 0);
}
else if (((player->pflags & PF_NIGHTSMODE) && (player->pflags & PF_DRILLING))
|| ((player->pflags & PF_JUMPED) && !(player->charflags & SF_NOJUMPDAMAGE && !(player->charability == CA_TWINSPIN && player->panim == PA_ABILITY)))
|| ((player->pflags & PF_JUMPED) && (player->pflags & PF_FORCEJUMPDAMAGE || !(player->charflags & SF_NOJUMPSPIN) || (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY)))
|| (player->pflags & (PF_SPINNING|PF_GLIDING))
|| (player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2)
|| ((player->charflags & SF_STOMPDAMAGE) && (P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0) && (P_MobjFlip(toucher)*toucher->momz < 0))
|| player->powers[pw_invulnerability] || player->powers[pw_super]) // Do you possess the ability to subdue the object?
{
if (P_MobjFlip(toucher)*toucher->momz < 0)
toucher->momz = -toucher->momz;
if ((P_MobjFlip(toucher)*toucher->momz < 0) && (elementalpierce != 1))
{
if (elementalpierce == 2)
P_DoBubbleBounce(player);
else
toucher->momz = -toucher->momz;
}
P_DamageMobj(special, toucher, toucher, 1, 0);
}
@ -1195,9 +1215,17 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
case MT_FIREFLOWER:
if (player->bot)
return;
player->powers[pw_shield] |= SH_FIREFLOWER;
toucher->color = SKINCOLOR_WHITE;
G_GhostAddColor(GHC_FIREFLOWER);
S_StartSound(toucher, sfx_mario3);
player->powers[pw_shield] = (player->powers[pw_shield] & SH_NOSTACK)|SH_FIREFLOWER;
if (!(player->powers[pw_super] || (mariomode && player->powers[pw_invulnerability])))
{
player->mo->color = SKINCOLOR_WHITE;
G_GhostAddColor(GHC_FIREFLOWER);
}
break;
// *************** //
@ -1366,7 +1394,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
return;
}
else if (((player->pflags & PF_NIGHTSMODE) && (player->pflags & PF_DRILLING))
|| ((player->pflags & PF_JUMPED) && !(player->charflags & SF_NOJUMPDAMAGE))
|| ((player->pflags & PF_JUMPED) && (player->pflags & PF_FORCEJUMPDAMAGE || !(player->charflags & SF_NOJUMPSPIN) || (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY)))
|| ((player->charflags & SF_STOMPDAMAGE) && (P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0) && (P_MobjFlip(toucher)*toucher->momz < 0))
|| (player->pflags & (PF_SPINNING|PF_GLIDING))
|| player->powers[pw_invulnerability] || player->powers[pw_super]) // Do you possess the ability to subdue the object?
@ -1478,7 +1506,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
return;
case MT_EXTRALARGEBUBBLE:
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL)
if (player->powers[pw_shield] & SH_PROTECTWATER)
return;
if (maptol & TOL_NIGHTS)
return;
@ -1604,8 +1632,10 @@ static void P_HitDeathMessages(player_t *player, mobj_t *inflictor, mobj_t *sour
else switch (inflictor->type)
{
case MT_PLAYER:
if (damagetype == DMG_NUKE) // SH_BOMB, armageddon shield
if (damagetype == DMG_NUKE) // SH_ARMAGEDDON, armageddon shield
str = M_GetText("%s%s's armageddon blast %s %s.\n");
else if ((inflictor->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL && (inflictor->player->pflags & PF_SHIELDABILITY))
str = M_GetText("%s%s's flame stomp %s %s.\n");
else if (inflictor->player->powers[pw_invulnerability])
str = M_GetText("%s%s's invincibility aura %s %s.\n");
else if (inflictor->player->powers[pw_super])
@ -2013,7 +2043,6 @@ boolean P_CheckRacers(void)
*/
void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype)
{
mobjtype_t item;
mobj_t *mo;
if (inflictor && (inflictor->type == MT_SHELL || inflictor->type == MT_FIREBALL))
@ -2229,81 +2258,80 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
if (source && target && target->player && source->player)
P_PlayVictorySound(source); // Killer laughs at you. LAUGHS! BWAHAHAHA!
#ifdef OLDANIMALSPAWNING
// Drop stuff.
// This determines the kind of object spawned
// during the death frame of a thing.
if (!mariomode // Don't show birds, etc. in Mario Mode Tails 12-23-2001
&& target->flags & MF_ENEMY)
{
if (cv_soniccd.value)
item = MT_SEED;
else
mobjtype_t item;
INT32 prandom;
switch (target->type)
{
INT32 prandom;
case MT_REDCRAWLA:
case MT_GOLDBUZZ:
case MT_SKIM:
case MT_UNIDUS:
item = MT_FLICKY_02/*MT_BUNNY*/;
break;
switch (target->type)
{
case MT_REDCRAWLA:
case MT_GOLDBUZZ:
case MT_SKIM:
case MT_UNIDUS:
item = MT_BUNNY;
break;
case MT_BLUECRAWLA:
case MT_JETTBOMBER:
case MT_GFZFISH:
item = MT_FLICKY_01/*MT_BIRD*/;
break;
case MT_BLUECRAWLA:
case MT_JETTBOMBER:
case MT_GFZFISH:
item = MT_BIRD;
break;
case MT_JETTGUNNER:
case MT_CRAWLACOMMANDER:
case MT_REDBUZZ:
case MT_DETON:
item = MT_FLICKY_12/*MT_MOUSE*/;
break;
case MT_JETTGUNNER:
case MT_CRAWLACOMMANDER:
case MT_REDBUZZ:
case MT_DETON:
item = MT_MOUSE;
break;
case MT_GSNAPPER:
case MT_EGGGUARD:
case MT_SPRINGSHELL:
item = MT_FLICKY_11/*MT_COW*/;
break;
case MT_GSNAPPER:
case MT_EGGGUARD:
case MT_SPRINGSHELL:
item = MT_COW;
break;
case MT_MINUS:
case MT_VULTURE:
case MT_POINTY:
case MT_YELLOWSHELL:
item = MT_FLICKY_03/*MT_CHICKEN*/;
break;
case MT_MINUS:
case MT_VULTURE:
case MT_POINTY:
case MT_YELLOWSHELL:
item = MT_CHICKEN;
break;
case MT_AQUABUZZ:
item = MT_FLICKY_01/*MT_REDBIRD*/;
break;
case MT_AQUABUZZ:
item = MT_REDBIRD;
break;
default:
if (target->info->doomednum)
prandom = target->info->doomednum%5; // "Random" animal for new enemies.
else
prandom = P_RandomKey(5); // No placable object, just use a random number.
default:
if (target->info->doomednum)
prandom = target->info->doomednum%5; // "Random" animal for new enemies.
else
prandom = P_RandomKey(5); // No placable object, just use a random number.
switch(prandom)
{
default: item = MT_BUNNY; break;
case 1: item = MT_BIRD; break;
case 2: item = MT_MOUSE; break;
case 3: item = MT_COW; break;
case 4: item = MT_CHICKEN; break;
}
break;
}
switch(prandom)
{
default: item = MT_FLICKY_02/*MT_BUNNY*/; break;
case 1: item = MT_FLICKY_01/*MT_BIRD*/; break;
case 2: item = MT_FLICKY_12/*MT_MOUSE*/; break;
case 3: item = MT_FLICKY_11/*MT_COW*/; break;
case 4: item = MT_FLICKY_03/*MT_CHICKEN*/; break;
}
break;
}
mo = P_SpawnMobj(target->x, target->y, target->z + (target->height / 2) - FixedMul(mobjinfo[item].height / 2, target->scale), item);
mo->destscale = target->scale;
P_SetScale(mo, mo->destscale);
}
else
#endif
// Other death animation effects
else switch(target->type)
switch(target->type)
{
case MT_BOUNCEPICKUP:
case MT_RAILPICKUP:
@ -2321,24 +2349,27 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
break;
case MT_PLAYER:
target->fuse = TICRATE*3; // timer before mobj disappears from view (even if not an actual player)
target->momx = target->momy = target->momz = 0;
if (damagetype == DMG_DROWNED) // drowned
{
target->movedir = damagetype; // we're MOVING the Damage Into anotheR function... Okay, this is a bit of a hack.
if (target->player->charflags & SF_MACHINE)
S_StartSound(target, sfx_fizzle);
target->fuse = TICRATE*3; // timer before mobj disappears from view (even if not an actual player)
target->momx = target->momy = target->momz = 0;
if (damagetype == DMG_DROWNED) // drowned
{
target->movedir = damagetype; // we're MOVING the Damage Into anotheR function... Okay, this is a bit of a hack.
if (target->player->charflags & SF_MACHINE)
S_StartSound(target, sfx_fizzle);
else
S_StartSound(target, sfx_drown);
// Don't jump up when drowning
}
else
S_StartSound(target, sfx_drown);
// Don't jump up when drowning
}
else
{
P_SetObjectMomZ(target, 14*FRACUNIT, false);
if ((source && source->type == MT_SPIKE) || damagetype == DMG_SPIKE) // Spikes
S_StartSound(target, sfx_spkdth);
else
P_PlayDeathSound(target);
{
P_SetObjectMomZ(target, 14*FRACUNIT, false);
if ((source && source->type == MT_SPIKE) || damagetype == DMG_SPIKE) // Spikes
S_StartSound(target, sfx_spkdth);
else
P_PlayDeathSound(target);
}
}
break;
default:
@ -2786,22 +2817,21 @@ void P_RemoveShield(player_t *player)
{
if (player->powers[pw_shield] & SH_FORCE)
{ // Multi-hit
if ((player->powers[pw_shield] & 0xFF) == 0)
player->powers[pw_shield] &= ~SH_FORCE;
else
if (player->powers[pw_shield] & SH_FORCEHP)
player->powers[pw_shield]--;
else
player->powers[pw_shield] &= SH_STACK;
}
else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_NONE)
{ // Second layer shields
player->powers[pw_shield] = SH_NONE;
// Reset fireflower
if (!player->powers[pw_super])
if (((player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER) && !(player->powers[pw_super] || (mariomode && player->powers[pw_invulnerability])))
{
player->mo->color = player->skincolor;
G_GhostAddColor(GHC_NORMAL);
}
player->powers[pw_shield] = SH_NONE;
}
else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_BOMB) // Give them what's coming to them!
else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ARMAGEDDON) // Give them what's coming to them!
{
P_BlackOw(player); // BAM!
player->pflags |= PF_JUMPDOWN;
@ -3027,12 +3057,15 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
switch (damagetype)
{
case DMG_WATER:
if (player->powers[pw_shield] & SH_PROTECTWATER)
return false; // Invincible to water damage
break;
case DMG_FIRE:
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL)
return false; // Invincible to water/fire damage
if (player->powers[pw_shield] & SH_PROTECTFIRE)
return false; // Invincible to fire damage
break;
case DMG_ELECTRIC:
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT)
if (player->powers[pw_shield] & SH_PROTECTELECTRIC)
return false; // Invincible to electric damage
break;
default:
@ -3061,7 +3094,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
if (!force && inflictor && inflictor->flags & MF_FIRE)
{
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL)
if (player->powers[pw_shield] & SH_PROTECTFIRE)
return false; // Invincible to fire objects
if (G_PlatformGametype() && inflictor && source && source->player)

View file

@ -59,9 +59,10 @@
#define AIMINGTOSLOPE(aiming) FINESINE((aiming>>ANGLETOFINESHIFT) & FINEMASK)
#define mariomode (maptol & TOL_MARIO)
#define twodlevel (maptol & TOL_2D)
#define mariomode (maptol & TOL_MARIO)
#define P_GetPlayerHeight(player) FixedMul(player->height, player->mo->scale)
#define P_GetPlayerSpinHeight(player) FixedMul(player->spinheight, player->mo->scale)
@ -143,6 +144,7 @@ boolean P_InQuicksand(mobj_t *mo);
void P_SetObjectMomZ(mobj_t *mo, fixed_t value, boolean relative);
void P_RestoreMusic(player_t *player);
void P_SpawnShieldOrb(player_t *player);
void P_SwitchShield(player_t *player, UINT16 shieldtype);
mobj_t *P_SpawnGhostMobj(mobj_t *mobj);
void P_GivePlayerRings(player_t *player, INT32 num_rings);
void P_GivePlayerLives(player_t *player, INT32 numlives);
@ -152,8 +154,9 @@ void P_ResetScore(player_t *player);
boolean P_AutoPause(void);
void P_DoJumpShield(player_t *player);
void P_DoBubbleBounce(player_t *player);
void P_BlackOw(player_t *player);
void P_ElementalFireTrail(player_t *player);
void P_ElementalFire(player_t *player, boolean cropcircle);
void P_DoPityCheck(player_t *player);
void P_PlayerThink(player_t *player);
@ -166,7 +169,7 @@ fixed_t P_ReturnThrustX(mobj_t *mo, angle_t angle, fixed_t move);
fixed_t P_ReturnThrustY(mobj_t *mo, angle_t angle, fixed_t move);
void P_InstaThrustEvenIn2D(mobj_t *mo, angle_t angle, fixed_t move);
boolean P_LookForEnemies(player_t *player);
boolean P_LookForEnemies(player_t *player, boolean nonenemies);
void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius);
void P_HomingAttack(mobj_t *source, mobj_t *enemy); /// \todo doesn't belong in p_user
boolean P_SuperReady(player_t *player);
@ -292,6 +295,11 @@ boolean P_CheckMissileRange(mobj_t *actor);
void P_NewChaseDir(mobj_t *actor);
boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed_t dist);
mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz, boolean lookforplayers);
void P_InternalFlickyBubble(mobj_t *actor);
void P_InternalFlickyFly(mobj_t *actor, fixed_t flyspeed, fixed_t targetdist, fixed_t chasez);
void P_InternalFlickyHop(mobj_t *actor, fixed_t momz, fixed_t momh, angle_t angle);
//
// P_MAP
//

View file

@ -203,7 +203,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
}
}
pflags = object->player->pflags & (PF_JUMPED|PF_SPINNING|PF_THOKKED); // I still need these.
pflags = object->player->pflags & (PF_JUMPED|PF_SPINNING|PF_THOKKED|PF_SHIELDABILITY); // I still need these.
jumping = object->player->jumping;
secondjump = object->player->secondjump;
P_ResetPlayer(object->player);
@ -969,10 +969,10 @@ static boolean PIT_CheckThing(mobj_t *thing)
{
if (G_RingSlingerGametype() && (!G_GametypeHasTeams() || tmthing->player->ctfteam != thing->player->ctfteam))
{
if ((tmthing->player->powers[pw_invulnerability] || tmthing->player->powers[pw_super])
if ((tmthing->player->powers[pw_invulnerability] || tmthing->player->powers[pw_super] || (((tmthing->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) && (tmthing->player->pflags & PF_SHIELDABILITY)))
&& !thing->player->powers[pw_super])
P_DamageMobj(thing, tmthing, tmthing, 1, 0);
else if ((thing->player->powers[pw_invulnerability] || thing->player->powers[pw_super])
else if ((thing->player->powers[pw_invulnerability] || thing->player->powers[pw_super] || (((thing->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) && (thing->player->pflags & PF_SHIELDABILITY)))
&& !tmthing->player->powers[pw_super])
P_DamageMobj(tmthing, thing, thing, 1, 0);
}
@ -1052,24 +1052,41 @@ static boolean PIT_CheckThing(mobj_t *thing)
else if (thing->z - FixedMul(FRACUNIT, thing->scale) <= tmthing->z + tmthing->height
&& thing->z + thing->height + FixedMul(FRACUNIT, thing->scale) >= tmthing->z)
{
// 0 = none, 1 = elemental pierce, 2 = bubble bounce
UINT8 elementalpierce = (((tmthing->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL || (tmthing->player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP) && (tmthing->player->pflags & PF_SHIELDABILITY)
? (((tmthing->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) ? 1 : 2)
: 0);
if (thing->flags & MF_MONITOR
&& (tmthing->player->pflags & (PF_SPINNING|PF_GLIDING)
|| ((tmthing->player->pflags & PF_JUMPED)
&& !(tmthing->player->charflags & SF_NOJUMPDAMAGE
&& !(tmthing->player->charability == CA_TWINSPIN && tmthing->player->panim == PA_ABILITY)))
&& (tmthing->player->pflags & PF_FORCEJUMPDAMAGE
|| !(tmthing->player->charflags & SF_NOJUMPSPIN)
|| (tmthing->player->charability == CA_TWINSPIN && tmthing->player->panim == PA_ABILITY)))
|| (tmthing->player->charability2 == CA2_MELEE && tmthing->player->panim == PA_ABILITY2)
|| ((tmthing->player->charflags & SF_STOMPDAMAGE)
&& (P_MobjFlip(tmthing)*(tmthing->z - (thing->z + thing->height/2)) > 0) && (P_MobjFlip(tmthing)*tmthing->momz < 0))))
&& (P_MobjFlip(tmthing)*(tmthing->z - (thing->z + thing->height/2)) > 0) && (P_MobjFlip(tmthing)*tmthing->momz < 0))
|| elementalpierce))
{
player_t *player = tmthing->player;
SINT8 flipval = P_MobjFlip(thing); // Save this value in case monitor gets removed.
fixed_t *momz = &tmthing->momz; // tmthing gets changed by P_DamageMobj, so we need a new pointer?! X_x;;
fixed_t *z = &tmthing->z; // aau.
P_DamageMobj(thing, tmthing, tmthing, 1, 0); // break the monitor
// Going down? Then bounce back up.
if ((P_MobjWasRemoved(thing) // Monitor was removed
|| !thing->health) // or otherwise popped
&& (flipval*(*momz) < 0)) // monitor is on the floor and you're going down, or on the ceiling and you're going up
*momz = -*momz; // Therefore, you should be thrust in the opposite direction, vertically.
return false;
&& (flipval*(*momz) < 0) // monitor is on the floor and you're going down, or on the ceiling and you're going up
&& (elementalpierce != 1)) // you're not piercing through the monitor...
{
if (elementalpierce == 2)
P_DoBubbleBounce(player);
else
*momz = -*momz; // Therefore, you should be thrust in the opposite direction, vertically.
}
if (!(elementalpierce == 1 && thing->flags & MF_GRENADEBOUNCE)) // prevent gold monitor clipthrough.
return false;
else
*z -= *momz; // to ensure proper collision.
}
}
}
@ -1084,8 +1101,9 @@ static boolean PIT_CheckThing(mobj_t *thing)
else if (thing->flags & MF_MONITOR && tmthing->player
&& (tmthing->player->pflags & (PF_SPINNING|PF_GLIDING)
|| ((tmthing->player->pflags & PF_JUMPED)
&& !(tmthing->player->charflags & SF_NOJUMPDAMAGE
&& !(tmthing->player->charability == CA_TWINSPIN && tmthing->player->panim == PA_ABILITY)))
&& (tmthing->player->pflags & PF_FORCEJUMPDAMAGE
|| !(tmthing->player->charflags & SF_NOJUMPSPIN)
|| (tmthing->player->charability == CA_TWINSPIN && tmthing->player->panim == PA_ABILITY)))
|| (tmthing->player->charability2 == CA2_MELEE && tmthing->player->panim == PA_ABILITY2)
|| ((tmthing->player->charflags & SF_STOMPDAMAGE)
&& (P_MobjFlip(tmthing)*(tmthing->z - (thing->z + thing->height/2)) > 0) && (P_MobjFlip(tmthing)*tmthing->momz < 0)))
@ -1124,6 +1142,9 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (tmthing->player && tmthing->z + tmthing->height > topz
&& tmthing->z + tmthing->height < tmthing->ceilingz)
{
if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->flags2 & MF2_STANDONME)) // Gold monitor hack...
return false;
tmfloorz = tmceilingz = INT32_MIN; // block while in air
#ifdef ESLOPE
tmceilingslope = NULL;
@ -1167,6 +1188,9 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (tmthing->player && tmthing->z < topz
&& tmthing->z > tmthing->floorz)
{
if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->flags2 & MF2_STANDONME)) // Gold monitor hack...
return false;
tmfloorz = tmceilingz = INT32_MAX; // block while in air
#ifdef ESLOPE
tmfloorslope = NULL;

View file

@ -572,51 +572,54 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj)
side_t *side = &sides[linedef->sidenum[0]];
fixed_t textop, texbottom, texheight;
fixed_t texmid, delta1, delta2;
INT32 texnum = R_GetTextureNum(side->midtexture); // make sure the texture is actually valid
// Get the midtexture's height
texheight = textures[texturetranslation[side->midtexture]]->height << FRACBITS;
if (texnum) {
// Get the midtexture's height
texheight = textures[texnum]->height << FRACBITS;
// Set texbottom and textop to the Z coordinates of the texture's boundaries
// Set texbottom and textop to the Z coordinates of the texture's boundaries
#if 0 // #ifdef POLYOBJECTS
// don't remove this code unless solid midtextures
// on non-solid polyobjects should NEVER happen in the future
if (linedef->polyobj && (linedef->polyobj->flags & POF_TESTHEIGHT)) {
if (linedef->flags & ML_EFFECT5 && !side->repeatcnt) { // "infinite" repeat
texbottom = back->floorheight + side->rowoffset;
textop = back->ceilingheight + side->rowoffset;
} else if (!!(linedef->flags & ML_DONTPEGBOTTOM) ^ !!(linedef->flags & ML_EFFECT3)) {
texbottom = back->floorheight + side->rowoffset;
textop = texbottom + texheight*(side->repeatcnt+1);
} else {
textop = back->ceilingheight + side->rowoffset;
texbottom = textop - texheight*(side->repeatcnt+1);
}
} else
// don't remove this code unless solid midtextures
// on non-solid polyobjects should NEVER happen in the future
if (linedef->polyobj && (linedef->polyobj->flags & POF_TESTHEIGHT)) {
if (linedef->flags & ML_EFFECT5 && !side->repeatcnt) { // "infinite" repeat
texbottom = back->floorheight + side->rowoffset;
textop = back->ceilingheight + side->rowoffset;
} else if (!!(linedef->flags & ML_DONTPEGBOTTOM) ^ !!(linedef->flags & ML_EFFECT3)) {
texbottom = back->floorheight + side->rowoffset;
textop = texbottom + texheight*(side->repeatcnt+1);
} else {
textop = back->ceilingheight + side->rowoffset;
texbottom = textop - texheight*(side->repeatcnt+1);
}
} else
#endif
{
if (linedef->flags & ML_EFFECT5 && !side->repeatcnt) { // "infinite" repeat
texbottom = openbottom + side->rowoffset;
textop = opentop + side->rowoffset;
} else if (!!(linedef->flags & ML_DONTPEGBOTTOM) ^ !!(linedef->flags & ML_EFFECT3)) {
texbottom = openbottom + side->rowoffset;
textop = texbottom + texheight*(side->repeatcnt+1);
} else {
textop = opentop + side->rowoffset;
texbottom = textop - texheight*(side->repeatcnt+1);
{
if (linedef->flags & ML_EFFECT5 && !side->repeatcnt) { // "infinite" repeat
texbottom = openbottom + side->rowoffset;
textop = opentop + side->rowoffset;
} else if (!!(linedef->flags & ML_DONTPEGBOTTOM) ^ !!(linedef->flags & ML_EFFECT3)) {
texbottom = openbottom + side->rowoffset;
textop = texbottom + texheight*(side->repeatcnt+1);
} else {
textop = opentop + side->rowoffset;
texbottom = textop - texheight*(side->repeatcnt+1);
}
}
}
texmid = texbottom+(textop-texbottom)/2;
texmid = texbottom+(textop-texbottom)/2;
delta1 = abs(mobj->z - texmid);
delta2 = abs(thingtop - texmid);
delta1 = abs(mobj->z - texmid);
delta2 = abs(thingtop - texmid);
if (delta1 > delta2) { // Below
if (opentop > texbottom)
opentop = texbottom;
} else { // Above
if (openbottom < textop)
openbottom = textop;
if (delta1 > delta2) { // Below
if (opentop > texbottom)
opentop = texbottom;
} else { // Above
if (openbottom < textop)
openbottom = textop;
}
}
}

View file

@ -2636,6 +2636,23 @@ static boolean P_ZMovement(mobj_t *mo)
mo->flags |= MF_NOGRAVITY;
}
break;
case MT_SPINFIRE:
if (P_CheckDeathPitCollide(mo))
{
P_RemoveMobj(mo);
return false;
}
if (mo->momz
&& !(mo->flags & MF_NOGRAVITY)
&& ((!(mo->eflags & MFE_VERTICALFLIP) && mo->z <= mo->floorz)
|| ((mo->eflags & MFE_VERTICALFLIP) && mo->z+mo->height >= mo->ceilingz)))
{
mo->flags |= MF_NOGRAVITY;
mo->momx = 8; // this is a hack which is used to ensure it still behaves as a missile and can damage others
mo->momy = mo->momz = 0;
mo->z = ((mo->eflags & MFE_VERTICALFLIP) ? mo->ceilingz-mo->height : mo->floorz);
}
break;
case MT_GOOP:
if (P_CheckDeathPitCollide(mo))
{
@ -3121,6 +3138,8 @@ static void P_PlayerZMovement(mobj_t *mo)
if (P_MobjFlip(mo)*mo->momz < 0) // falling
{
boolean clipmomz = !(P_CheckDeathPitCollide(mo));
mo->pmomz = 0; // We're on a new floor, don't keep doing platform movement.
// Squat down. Decrease viewheight for a moment after hitting the ground (hard),
@ -3250,23 +3269,56 @@ static void P_PlayerZMovement(mobj_t *mo)
mo->player->pflags &= ~PF_SPINNING;
if (!(mo->player->pflags & PF_GLIDING))
mo->player->pflags &= ~PF_JUMPED;
mo->player->pflags &= ~(PF_JUMPED|PF_FORCEJUMPDAMAGE);
mo->player->pflags &= ~(PF_THOKKED|PF_CANCARRY/*|PF_GLIDING*/);
mo->player->jumping = 0;
mo->player->secondjump = 0;
mo->player->glidetime = 0;
mo->player->climbing = 0;
mo->player->powers[pw_tailsfly] = 0;
if (mo->player->pflags & PF_SHIELDABILITY)
{
mo->player->pflags &= ~PF_SHIELDABILITY;
if ((mo->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) // Elemental shield's stomp attack.
{
if (mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) // play a blunt sound
S_StartSound(mo, sfx_s3k4c);
else // create a fire pattern on the ground
{
S_StartSound(mo, sfx_s3k47);
P_ElementalFire(mo->player, true);
}
P_SetObjectMomZ(mo,
(mo->eflags & MFE_UNDERWATER)
? 6*FRACUNIT/5
: 5*FRACUNIT/2,
false);
P_SetPlayerMobjState(mo, S_PLAY_FALL);
mo->momx = mo->momy = 0;
clipmomz = false;
}
else if ((mo->player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP) // Bubble shield's bounce attack.
{
P_DoBubbleBounce(mo->player);
clipmomz = false;
}
}
}
}
if (!(mo->player->pflags & PF_SPINNING))
mo->player->pflags &= ~PF_STARTDASH;
if (tmfloorthing && (tmfloorthing->flags & (MF_PUSHABLE|MF_MONITOR)
|| tmfloorthing->flags2 & MF2_STANDONME || tmfloorthing->type == MT_PLAYER))
mo->momz = tmfloorthing->momz;
else if (!tmfloorthing)
mo->momz = 0;
if (clipmomz)
{
if (tmfloorthing && (tmfloorthing->flags & (MF_PUSHABLE|MF_MONITOR)
|| tmfloorthing->flags2 & MF2_STANDONME || tmfloorthing->type == MT_PLAYER))
mo->momz = tmfloorthing->momz;
else if (!tmfloorthing)
mo->momz = 0;
}
}
else if (tmfloorthing && (tmfloorthing->flags & (MF_PUSHABLE|MF_MONITOR)
|| tmfloorthing->flags2 & MF2_STANDONME || tmfloorthing->type == MT_PLAYER))
@ -3430,17 +3482,14 @@ static boolean P_SceneryZMovement(mobj_t *mo)
if ((!(mo->eflags & MFE_VERTICALFLIP) && mo->z <= mo->floorz)
|| (mo->eflags & MFE_VERTICALFLIP && mo->z+mo->height >= mo->ceilingz))
{
// DO NOT use random numbers here.
// SonicCD mode is console togglable and
// affects demos.
UINT8 rltime = (leveltime & 4);
if (!rltime)
P_SpawnMobj(mo->x, mo->y, mo->floorz, MT_GFZFLOWER3);
else if (rltime == 2)
P_SpawnMobj(mo->x, mo->y, mo->floorz, MT_GFZFLOWER2);
else
P_SpawnMobj(mo->x, mo->y, mo->floorz, MT_GFZFLOWER1);
mobjtype_t flowertype = ((P_RandomChance(FRACUNIT/2)) ? MT_GFZFLOWER1 : MT_GFZFLOWER3);
mobj_t *flower = P_SpawnMobjFromMobj(mo, 0, 0, 0, flowertype);
if (flower)
{
P_SetScale(flower, mo->scale/16);
flower->destscale = mo->scale;
flower->scalespeed = mo->scale/8;
}
P_RemoveMobj(mo);
return false;
@ -3538,6 +3587,7 @@ void P_MobjCheckWater(mobj_t *mobj)
ffloor_t *rover;
player_t *p = mobj->player; // Will just be null if not a player.
fixed_t height = (p ? P_GetPlayerHeight(p) : mobj->height); // for players, calculation height does not necessarily match actual height for gameplay reasons (spin, etc)
boolean wasgroundpounding = (p && (mobj->eflags & MFE_GOOWATER) && ((p->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL || (p->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP) && (p->pflags & PF_SHIELDABILITY));
// Default if no water exists.
mobj->watertop = mobj->waterbottom = mobj->z - 1000*FRACUNIT;
@ -3608,15 +3658,24 @@ void P_MobjCheckWater(mobj_t *mobj)
{
if (!((p->powers[pw_super]) || (p->powers[pw_invulnerability])))
{
if ((p->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT)
{ // Water removes attract shield.
boolean electric = !!(p->powers[pw_shield] & SH_PROTECTELECTRIC);
#define SH_OP (SH_PROTECTFIRE|SH_PROTECTWATER|SH_PROTECTELECTRIC)
if ((p->powers[pw_shield] & SH_OP) == SH_OP) // No.
P_KillMobj(mobj, NULL, NULL, DMG_INSTAKILL);
#undef SH_OP
else if (electric || ((p->powers[pw_shield] & SH_PROTECTFIRE) && !(p->powers[pw_shield] & SH_PROTECTWATER)))
{ // Water removes electric and non-water fire shields...
P_FlashPal(p,
electric
? PAL_WHITE
: PAL_NUKE,
1);
p->powers[pw_shield] = p->powers[pw_shield] & SH_STACK;
P_FlashPal(p, PAL_WHITE, 1);
}
}
// Drown timer setting
if ((p->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL // Has elemental
if ((p->powers[pw_shield] & SH_PROTECTWATER) // Has water protection
|| (p->exiting) // Or exiting
|| (maptol & TOL_NIGHTS) // Or in NiGHTS mode
|| (mariomode)) // Or in Mario mode...
@ -3629,6 +3688,12 @@ void P_MobjCheckWater(mobj_t *mobj)
// Then we'll set it!
p->powers[pw_underwater] = underwatertics + 1;
}
if (wasgroundpounding)
{
p->pflags &= ~PF_SHIELDABILITY;
mobj->momz >>= 1;
}
}
// The rest of this code only executes on a water state change.
@ -3646,7 +3711,7 @@ void P_MobjCheckWater(mobj_t *mobj)
|| ((mobj->eflags & MFE_VERTICALFLIP) && mobj->ceilingz-mobj->waterbottom <= height>>1))
return;
if ((mobj->eflags & MFE_GOOWATER || wasingoo)) { // Decide what happens to your momentum when you enter/leave goopy water.
if (!wasgroundpounding && (mobj->eflags & MFE_GOOWATER || wasingoo)) { // Decide what happens to your momentum when you enter/leave goopy water.
if (P_MobjFlip(mobj)*mobj->momz < 0) // You are entering the goo?
mobj->momz = FixedMul(mobj->momz, FixedDiv(2*FRACUNIT, 5*FRACUNIT)); // kill momentum significantly, to make the goo feel thick.
}
@ -4865,7 +4930,7 @@ static void P_Boss4MoveSpikeballs(mobj_t *mobj, angle_t angle, fixed_t fz)
{
INT32 s;
mobj_t *base = mobj, *seg;
fixed_t dist, bz = (mobj->spawnpoint->z+16)<<FRACBITS;
fixed_t dist, bz = mobj->watertop+(16<<FRACBITS);
while ((base = base->tracer))
{
for (seg = base, dist = 172*FRACUNIT, s = 9; seg; seg = seg->hnext, dist += 124*FRACUNIT, --s)
@ -4879,7 +4944,7 @@ static void P_Boss4PinchSpikeballs(mobj_t *mobj, angle_t angle, fixed_t fz)
{
INT32 s;
mobj_t *base = mobj, *seg;
fixed_t dist, bz = (mobj->spawnpoint->z+16)<<FRACBITS;
fixed_t dist, bz = mobj->watertop+(16<<FRACBITS);
while ((base = base->tracer))
{
for (seg = base, dist = 112*FRACUNIT, s = 9; seg; seg = seg->hnext, dist += 132*FRACUNIT, --s)
@ -4995,7 +5060,7 @@ static void P_Boss4Thinker(mobj_t *mobj)
INT32 i, arm;
mobj_t *seg, *base = mobj;
// First frame init, spawn all the things.
mobj->spawnpoint->z = mobj->z>>FRACBITS;
mobj->watertop = mobj->z;
z = mobj->z + mobj->height/2 - mobjinfo[MT_EGGMOBILE4_MACE].height/2;
for (arm = 0; arm <3 ; arm++)
{
@ -5051,7 +5116,7 @@ static void P_Boss4Thinker(mobj_t *mobj)
case 3:
{
fixed_t z;
if (mobj->z < (mobj->spawnpoint->z+512)<<FRACBITS)
if (mobj->z < mobj->watertop+(512<<FRACBITS))
mobj->momz = 8*FRACUNIT;
else
{
@ -5060,7 +5125,7 @@ static void P_Boss4Thinker(mobj_t *mobj)
}
mobj->movecount += 400<<(FRACBITS>>1);
mobj->movecount %= 360*FRACUNIT;
z = mobj->z - (mobj->spawnpoint->z<<FRACBITS) - mobjinfo[MT_EGGMOBILE4_MACE].height - mobj->height/2;
z = mobj->z - mobj->watertop - mobjinfo[MT_EGGMOBILE4_MACE].height - mobj->height/2;
if (z < 0) // We haven't risen high enough to pull the spikeballs along yet
P_Boss4MoveSpikeballs(mobj, FixedAngle(mobj->movecount), 0); // So don't pull the spikeballs along yet.
else
@ -5070,13 +5135,13 @@ static void P_Boss4Thinker(mobj_t *mobj)
// Pinch phase!
case 4:
{
if (mobj->z < (mobj->spawnpoint->z+512+128*(mobj->info->damage-mobj->health))<<FRACBITS)
if (mobj->z < (mobj->watertop + ((512+128*(mobj->info->damage-mobj->health))<<FRACBITS)))
mobj->momz = 8*FRACUNIT;
else
mobj->momz = 0;
mobj->movecount += (800+800*(mobj->info->damage-mobj->health))<<(FRACBITS>>1);
mobj->movecount %= 360*FRACUNIT;
P_Boss4PinchSpikeballs(mobj, FixedAngle(mobj->movecount), mobj->z - (mobj->spawnpoint->z<<FRACBITS) - mobjinfo[MT_EGGMOBILE4_MACE].height - mobj->height/2);
P_Boss4PinchSpikeballs(mobj, FixedAngle(mobj->movecount), mobj->z - mobj->watertop - mobjinfo[MT_EGGMOBILE4_MACE].height - mobj->height/2);
if (!mobj->target || !mobj->target->health)
P_SupermanLook4Players(mobj);
@ -6318,7 +6383,7 @@ static boolean P_ShieldLook(mobj_t *thing, shieldtype_t shield)
// TODO: Make an MT_SHIELDORB which changes color/states to always match the appropriate shield,
// instead of having completely seperate mobjtypes.
if (shield != SH_FORCE)
if (!(shield & SH_FORCE))
{ // Regular shields check for themselves only
if ((shieldtype_t)(thing->target->player->powers[pw_shield] & SH_NOSTACK) != shield)
{
@ -6332,9 +6397,9 @@ static boolean P_ShieldLook(mobj_t *thing, shieldtype_t shield)
return false;
}
if (shield == SH_FORCE && thing->movecount != (thing->target->player->powers[pw_shield] & 0xFF))
if (shield & SH_FORCE && thing->movecount != (thing->target->player->powers[pw_shield] & SH_FORCEHP))
{
thing->movecount = (thing->target->player->powers[pw_shield] & 0xFF);
thing->movecount = (thing->target->player->powers[pw_shield] & SH_FORCEHP);
if (thing->movecount < 1)
{
if (thing->info->painstate)
@ -6355,13 +6420,14 @@ static boolean P_ShieldLook(mobj_t *thing, shieldtype_t shield)
thing->eflags = (thing->eflags & ~MFE_VERTICALFLIP)|(thing->target->eflags & MFE_VERTICALFLIP);
P_SetScale(thing, FixedMul(thing->target->scale, thing->target->player->shieldscale));
thing->destscale = thing->scale;
P_UnsetThingPosition(thing);
thing->x = thing->target->x;
thing->y = thing->target->y;
if (thing->eflags & MFE_VERTICALFLIP)
thing->z = thing->target->z + thing->target->height - thing->height + FixedDiv(P_GetPlayerHeight(thing->target->player) - thing->target->height, 3*FRACUNIT) - FixedMul(2*FRACUNIT, thing->target->scale);
thing->z = thing->target->z + (thing->target->height - thing->height + FixedDiv(P_GetPlayerHeight(thing->target->player) - thing->target->height, 3*FRACUNIT)) - FixedMul(2*FRACUNIT, thing->target->scale);
else
thing->z = thing->target->z - FixedDiv(P_GetPlayerHeight(thing->target->player) - thing->target->height, 3*FRACUNIT) + FixedMul(2*FRACUNIT, thing->target->scale);
thing->z = thing->target->z - (FixedDiv(P_GetPlayerHeight(thing->target->player) - thing->target->height, 3*FRACUNIT)) + FixedMul(2*FRACUNIT, thing->target->scale);
P_SetThingPosition(thing);
P_CheckPosition(thing, thing->x, thing->y);
@ -6384,7 +6450,7 @@ void P_RunShields(void)
// run shields
for (i = 0; i < numshields; i++)
{
P_ShieldLook(shields[i], shields[i]->info->speed);
P_ShieldLook(shields[i], shields[i]->threshold);
P_SetTarget(&shields[i], NULL);
}
numshields = 0;
@ -6392,7 +6458,7 @@ void P_RunShields(void)
static boolean P_AddShield(mobj_t *thing)
{
shieldtype_t shield = thing->info->speed;
shieldtype_t shield = thing->threshold;
if (!thing->target || thing->target->health <= 0 || !thing->target->player
|| (thing->target->player->powers[pw_shield] & SH_NOSTACK) == SH_NONE || thing->target->player->powers[pw_super]
@ -6402,7 +6468,7 @@ static boolean P_AddShield(mobj_t *thing)
return false;
}
if (shield != SH_FORCE)
if (!(shield & SH_FORCE))
{ // Regular shields check for themselves only
if ((shieldtype_t)(thing->target->player->powers[pw_shield] & SH_NOSTACK) != shield)
{
@ -6440,6 +6506,13 @@ void P_RunOverlays(void)
if (!mo->target)
continue;
if (P_MobjWasRemoved(mo->target))
{
P_RemoveMobj(mo);
continue;
}
if (!splitscreen /*&& rendermode != render_soft*/)
{
angle_t viewingangle;
@ -6619,7 +6692,7 @@ void P_MobjThinker(mobj_t *mobj)
fixed_t oldheight = mobj->height;
UINT8 correctionType = 0; // Don't correct Z position, just gain height
if (mobj->z > mobj->floorz && mobj->z + mobj->height < mobj->ceilingz
if ((mobj->flags & MF_NOCLIPHEIGHT || (mobj->z > mobj->floorz && mobj->z + mobj->height < mobj->ceilingz))
&& mobj->type != MT_EGGMOBILE_FIRE)
correctionType = 1; // Correct Z position by centering
else if (mobj->eflags & MFE_VERTICALFLIP)
@ -6664,6 +6737,11 @@ void P_MobjThinker(mobj_t *mobj)
if (P_MobjWasRemoved(mobj))
return;
#endif
if (mobj->flags2 & MF2_SHIELD)
if (!P_AddShield(mobj))
return;
switch (mobj->type)
{
case MT_HOOP:
@ -6720,15 +6798,121 @@ void P_MobjThinker(mobj_t *mobj)
else
P_AddOverlay(mobj);
break;
case MT_BLACKORB:
case MT_WHITEORB:
case MT_GREENORB:
case MT_YELLOWORB:
case MT_BLUEORB:
case MT_PITYORB:
if (!P_AddShield(mobj))
case MT_PITY_ORB:
case MT_WHIRLWIND_ORB:
case MT_ARMAGEDDON_ORB:
if (!(mobj->flags2 & MF2_SHIELD))
return;
break;
case MT_ATTRACT_ORB:
if (!(mobj->flags2 & MF2_SHIELD))
return;
if (/*(mobj->target) -- the following is implicit by P_AddShield
&& (mobj->target->player)
&& */ (mobj->target->player->homing))
{
P_SetMobjState(mobj, mobj->info->painstate);
mobj->tics++;
}
break;
case MT_ELEMENTAL_ORB:
if (!(mobj->flags2 & MF2_SHIELD))
return;
if (mobj->tracer
/* && mobj->target -- the following is implicit by P_AddShield
&& mobj->target->player
&& (mobj->target->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL */
&& mobj->target->player->pflags & PF_SHIELDABILITY
&& ((statenum_t)(mobj->tracer->state-states) < mobj->info->raisestate
|| (mobj->tracer->state->nextstate < mobj->info->raisestate && mobj->tracer->tics == 1)))
{
P_SetMobjState(mobj, mobj->info->painstate);
mobj->tics++;
P_SetMobjState(mobj->tracer, mobj->info->raisestate);
mobj->tracer->tics++;
}
break;
case MT_FORCE_ORB:
if (!(mobj->flags2 & MF2_SHIELD))
return;
if (/*
&& mobj->target -- the following is implicit by P_AddShield
&& mobj->target->player
&& (mobj->target->player->powers[pw_shield] & SH_FORCE)
&& */ (mobj->target->player->pflags & PF_SHIELDABILITY))
{
mobj_t *whoosh = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_GHOST); // done here so the offset is correct
P_SetMobjState(whoosh, mobj->info->raisestate);
whoosh->destscale = whoosh->scale<<1;
whoosh->scalespeed = FixedMul(whoosh->scalespeed, whoosh->scale);
whoosh->height = 38*whoosh->scale;
whoosh->fuse = 10;
whoosh->flags |= MF_NOCLIPHEIGHT;
whoosh->momz = mobj->target->momz; // Stay reasonably centered for a few frames
mobj->target->player->pflags &= ~PF_SHIELDABILITY; // prevent eternal whoosh
}
case MT_FLAMEAURA_ORB:
if (!(mobj->flags2 & MF2_SHIELD))
return;
mobj->angle = mobj->target->angle; // implicitly okay because of P_AddShield
if (mobj->tracer
/* && mobj->target -- the following is implicit by P_AddShield
&& mobj->target->player
&& (mobj->target->player->powers[pw_shield] & SH_NOSTACK) == SH_FLAMEAURA */
&& mobj->target->player->pflags & PF_SHIELDABILITY
&& ((statenum_t)(mobj->tracer->state-states) < mobj->info->raisestate
|| (mobj->tracer->state->nextstate < mobj->info->raisestate && mobj->tracer->tics == 1)))
{
P_SetMobjState(mobj, mobj->info->painstate);
mobj->tics++;
P_SetMobjState(mobj->tracer, mobj->info->raisestate);
mobj->tracer->tics++;
}
break;
case MT_BUBBLEWRAP_ORB:
if (!(mobj->flags2 & MF2_SHIELD))
return;
if (mobj->tracer
/* && mobj->target -- the following is implicit by P_AddShield
&& mobj->target->player
&& (mobj->target->player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP */
)
{
if (mobj->target->player->pflags & PF_SHIELDABILITY
&& ((statenum_t)(mobj->state-states) < mobj->info->painstate
|| (mobj->state->nextstate < mobj->info->painstate && mobj->tics == 1)))
{
P_SetMobjState(mobj, mobj->info->painstate);
mobj->tics++;
P_SetMobjState(mobj->tracer, mobj->info->raisestate);
mobj->tracer->tics++;
}
else if (mobj->target->eflags & MFE_JUSTHITFLOOR
&& (statenum_t)(mobj->state-states) == mobj->info->painstate)
{
P_SetMobjState(mobj, mobj->info->painstate+1);
mobj->tics++;
P_SetMobjState(mobj->tracer, mobj->info->raisestate+1);
mobj->tracer->tics++;
}
}
break;
case MT_THUNDERCOIN_ORB:
if (!(mobj->flags2 & MF2_SHIELD))
return;
if (mobj->tracer
/* && mobj->target -- the following is implicit by P_AddShield
&& mobj->target->player
&& (mobj->target->player->powers[pw_shield] & SH_NOSTACK) == SH_THUNDERCOIN */
&& (mobj->target->player->pflags & PF_SHIELDABILITY))
{
P_SetMobjState(mobj, mobj->info->painstate);
mobj->tics++;
P_SetMobjState(mobj->tracer, mobj->info->raisestate);
mobj->tracer->tics++;
mobj->target->player->pflags &= ~PF_SHIELDABILITY; // prevent eternal spark
}
break;
case MT_WATERDROP:
P_SceneryCheckWater(mobj);
if ((mobj->z <= mobj->floorz || mobj->z <= mobj->watertop)
@ -6862,7 +7046,8 @@ void P_MobjThinker(mobj_t *mobj)
}
break;
case MT_SEED:
mobj->momz = mobj->info->speed;
if (P_MobjFlip(mobj)*mobj->momz < mobj->info->speed)
mobj->momz = P_MobjFlip(mobj)*mobj->info->speed;
break;
case MT_ROCKCRUMBLE1:
case MT_ROCKCRUMBLE2:
@ -7011,14 +7196,14 @@ void P_MobjThinker(mobj_t *mobj)
if (mobj->fuse > 0 && mobj->fuse < 2*TICRATE-(TICRATE/7)
&& (mobj->fuse & 3))
{
INT32 i,j;
INT32 i;
fixed_t x,y,z;
fixed_t ns;
mobj_t *mo2;
mobj_t *flicky;
i = P_RandomByte();
z = mobj->subsector->sector->floorheight + ((P_RandomByte()&63)*FRACUNIT);
for (j = 0; j < 2; j++)
for (i = 0; i < 2; i++)
{
const angle_t fa = (P_RandomByte()*FINEANGLES/16) & FINEMASK;
ns = 64 * FRACUNIT;
@ -7026,25 +7211,22 @@ void P_MobjThinker(mobj_t *mobj)
y = mobj->y + FixedMul(FINECOSINE(fa),ns);
mo2 = P_SpawnMobj(x, y, z, MT_EXPLODE);
P_SetMobjStateNF(mo2, S_XPLD_EGGTRAP); // so the flickies don't lose their target if they spawn
ns = 4 * FRACUNIT;
mo2->momx = FixedMul(FINESINE(fa),ns);
mo2->momy = FixedMul(FINECOSINE(fa),ns);
mo2->angle = fa << ANGLETOFINESHIFT;
i = P_RandomByte();
if (i % 5 == 0)
P_SpawnMobj(x, y, z, MT_CHICKEN);
else if (i % 4 == 0)
P_SpawnMobj(x, y, z, MT_COW);
else if (i % 3 == 0)
{
P_SpawnMobj(x, y, z, MT_BIRD);
if (P_RandomChance(FRACUNIT/4)) // I filled a spreadsheet trying to get the equivalent chance to the original P_RandomByte hack!
S_StartSound(mo2, mobj->info->deathsound);
}
else if ((i & 1) == 0)
P_SpawnMobj(x, y, z, MT_BUNNY);
else
P_SpawnMobj(x, y, z, MT_MOUSE);
flicky = P_InternalFlickySpawn(mo2, 0, 8*FRACUNIT, false);
if (!flicky)
break;
P_SetTarget(&flicky->target, mo2);
flicky->momx = mo2->momx;
flicky->momy = mo2->momy;
}
mobj->fuse--;
@ -7105,6 +7287,8 @@ void P_MobjThinker(mobj_t *mobj)
}
break;
case MT_AQUABUZZ:
P_MobjCheckWater(mobj); // solely for MFE_UNDERWATER for A_FlickySpawn
// no break here on purpose
case MT_BIGAIRMINE:
{
if (mobj->tracer && mobj->tracer->player && mobj->tracer->health > 0
@ -7563,11 +7747,26 @@ void P_MobjThinker(mobj_t *mobj)
mobj->tracer->y, mobj->tracer->floorz, SPLATDRAWMODE_SHADE);
#endif
break;
case MT_SPINDUST: // Spindash dust
mobj->momx = FixedMul(mobj->momx, (3*FRACUNIT)/4); // originally 50000
mobj->momy = FixedMul(mobj->momy, (3*FRACUNIT)/4); // same
//mobj->momz = mobj->momz+P_MobjFlip(mobj)/3; // no meaningful change in value to be frank
if (mobj->state >= &states[S_SPINDUST_BUBBLE1] && mobj->state <= &states[S_SPINDUST_BUBBLE4]) // bubble dust!
{
P_MobjCheckWater(mobj);
if (mobj->watertop != mobj->subsector->sector->floorheight - 1000*FRACUNIT
&& mobj->z+mobj->height >= mobj->watertop - 5*FRACUNIT)
mobj->flags2 |= MF2_DONTDRAW;
}
break;
case MT_SPINFIRE:
if (mobj->eflags & MFE_VERTICALFLIP)
mobj->z = mobj->ceilingz - mobj->height;
else
mobj->z = mobj->floorz;
if (mobj->flags & MF_NOGRAVITY)
{
if (mobj->eflags & MFE_VERTICALFLIP)
mobj->z = mobj->ceilingz - mobj->height;
else
mobj->z = mobj->floorz;
}
// THERE IS NO BREAK HERE ON PURPOSE
default:
// check mobj against possible water content, before movement code
@ -8108,6 +8307,10 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
if (mobj->type == MT_UNIDUS)
mobj->z -= FixedMul(mobj->info->mass, mobj->scale);
// defaults onground
if (mobj->z + mobj->height == mobj->ceilingz)
mobj->eflags |= MFE_ONGROUND;
}
else
mobj->z = z;
@ -8196,13 +8399,8 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
// Special condition for the 2nd boss.
mobj->watertop = mobj->info->speed;
break;
case MT_BIRD:
case MT_BUNNY:
case MT_MOUSE:
case MT_CHICKEN:
case MT_COW:
case MT_REDBIRD:
mobj->fuse = P_RandomRange(300, 350);
case MT_FLICKY_08:
mobj->color = (P_RandomChance(FRACUNIT/2) ? SKINCOLOR_RED : SKINCOLOR_AQUA);
break;
case MT_REDRING: // Make MT_REDRING red by default
mobj->color = skincolor_redring;

View file

@ -193,6 +193,7 @@ typedef enum
MF2_BOSSDEAD = 1<<26, // Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.)
MF2_AMBUSH = 1<<27, // Alternate behaviour typically set by MTF_AMBUSH
MF2_LINKDRAW = 1<<28, // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position)
MF2_SHIELD = 1<<29, // Thinker calls P_AddShield/P_ShieldLook (must be partnered with MF_SCENERY to use)
// free: to and including 1<<31
} mobjflag2_t;

View file

@ -470,6 +470,7 @@ static void P_NetUnArchivePlayers(void)
#define SD_TAG 0x10
#define SD_FLOORANG 0x20
#define SD_CEILANG 0x40
#define SD_TAGLIST 0x80
#define LD_FLAG 0x01
#define LD_SPECIAL 0x02
@ -519,10 +520,9 @@ static void P_NetArchiveWorld(void)
//
// flats
//
// P_AddLevelFlat should not add but just return the number
if (ss->floorpic != P_AddLevelFlat(ms->floorpic, levelflats))
if (ss->floorpic != P_CheckLevelFlat(ms->floorpic))
diff |= SD_FLOORPIC;
if (ss->ceilingpic != P_AddLevelFlat(ms->ceilingpic, levelflats))
if (ss->ceilingpic != P_CheckLevelFlat(ms->ceilingpic))
diff |= SD_CEILPIC;
if (ss->lightlevel != SHORT(ms->lightlevel))
@ -545,6 +545,8 @@ static void P_NetArchiveWorld(void)
if (ss->tag != SHORT(ms->tag))
diff2 |= SD_TAG;
if (ss->nexttag != ss->spawn_nexttag || ss->firsttag != ss->spawn_firsttag)
diff2 |= SD_TAGLIST;
// Check if any of the sector's FOFs differ from how they spawned
if (ss->ffloors)
@ -592,16 +594,17 @@ static void P_NetArchiveWorld(void)
WRITEFIXED(put, ss->ceiling_xoffs);
if (diff2 & SD_CYOFFS)
WRITEFIXED(put, ss->ceiling_yoffs);
if (diff2 & SD_TAG)
{
if (diff2 & SD_TAG) // save only the tag
WRITEINT16(put, ss->tag);
WRITEINT32(put, ss->firsttag);
WRITEINT32(put, ss->nexttag);
}
if (diff2 & SD_FLOORANG)
WRITEANGLE(put, ss->floorpic_angle);
if (diff2 & SD_CEILANG)
WRITEANGLE(put, ss->ceilingpic_angle);
if (diff2 & SD_TAGLIST) // save both firsttag and nexttag
{ // either of these could be changed even if tag isn't
WRITEINT32(put, ss->firsttag);
WRITEINT32(put, ss->nexttag);
}
// Special case: save the stats of all modified ffloors along with their ffloor "number"s
// we don't bother with ffloors that haven't changed, that would just add to savegame even more than is really needed
@ -762,12 +765,12 @@ static void P_NetUnArchiveWorld(void)
sectors[i].ceilingheight = READFIXED(get);
if (diff & SD_FLOORPIC)
{
sectors[i].floorpic = P_AddLevelFlat((char *)get, levelflats);
sectors[i].floorpic = P_AddLevelFlatRuntime((char *)get);
get += 8;
}
if (diff & SD_CEILPIC)
{
sectors[i].ceilingpic = P_AddLevelFlat((char *)get, levelflats);
sectors[i].ceilingpic = P_AddLevelFlatRuntime((char *)get);
get += 8;
}
if (diff & SD_LIGHT)
@ -784,12 +787,11 @@ static void P_NetUnArchiveWorld(void)
if (diff2 & SD_CYOFFS)
sectors[i].ceiling_yoffs = READFIXED(get);
if (diff2 & SD_TAG)
sectors[i].tag = READINT16(get); // DON'T use P_ChangeSectorTag
if (diff2 & SD_TAGLIST)
{
INT16 tag;
tag = READINT16(get);
sectors[i].firsttag = READINT32(get);
sectors[i].nexttag = READINT32(get);
P_ChangeSectorTag(i, tag);
}
if (diff2 & SD_FLOORANG)
sectors[i].floorpic_angle = READANGLE(get);
@ -2628,6 +2630,7 @@ static void P_NetUnArchiveThinkers(void)
thinker_t *next;
UINT8 tclass;
UINT8 restoreNum = false;
UINT32 i;
if (READUINT32(save_p) != ARCHIVEBLOCK_THINKERS)
I_Error("Bad $$$.sav at archive block Thinkers");
@ -2648,6 +2651,12 @@ static void P_NetUnArchiveThinkers(void)
iquetail = iquehead = 0;
P_InitThinkers();
// clear sector thinker pointers so they don't point to non-existant thinkers for all of eternity
for (i = 0; i < numsectors; i++)
{
sectors[i].floordata = sectors[i].ceilingdata = sectors[i].lightingdata = NULL;
}
// read in saved thinkers
for (;;)
{
@ -3306,7 +3315,7 @@ void P_SaveNetGame(void)
{
thinker_t *th;
mobj_t *mobj;
INT32 i = 0;
INT32 i = 1; // don't start from 0, it'd be confused with a blank pointer otherwise
CV_SaveNetVars(&save_p);
P_NetArchiveMisc();

View file

@ -160,6 +160,33 @@ FUNCNORETURN static ATTRNORETURN void CorruptMapError(const char *msg)
I_Error("Invalid or corrupt map.\nLook in log file or text console for technical details.");
}
/** Sets a header's flickies to be equivalent to the original Freed Animals
*
* \param i The header to set flickies for
*/
void P_SetDemoFlickies(INT16 i)
{
mapheaderinfo[i]->numFlickies = 5;
mapheaderinfo[i]->flickies = Z_Realloc(mapheaderinfo[i]->flickies, 5*sizeof(mobjtype_t), PU_STATIC, NULL);
mapheaderinfo[i]->flickies[0] = MT_FLICKY_02/*MT_BUNNY*/;
mapheaderinfo[i]->flickies[1] = MT_FLICKY_01/*MT_BIRD*/;
mapheaderinfo[i]->flickies[2] = MT_FLICKY_12/*MT_MOUSE*/;
mapheaderinfo[i]->flickies[3] = MT_FLICKY_11/*MT_COW*/;
mapheaderinfo[i]->flickies[4] = MT_FLICKY_03/*MT_CHICKEN*/;
}
/** Clears a header's flickies
*
* \param i The header to clear flickies for
*/
void P_DeleteFlickies(INT16 i)
{
if (mapheaderinfo[i]->flickies)
Z_Free(mapheaderinfo[i]->flickies);
mapheaderinfo[i]->flickies = NULL;
mapheaderinfo[i]->numFlickies = 0;
}
#define NUMLAPS_DEFAULT 4
/** Clears the data from a single map header.
@ -223,6 +250,12 @@ static void P_ClearSingleMapHeaderInfo(INT16 i)
mapheaderinfo[num]->levelflags = 0;
DEH_WriteUndoline("MENUFLAGS", va("%d", mapheaderinfo[num]->menuflags), UNDO_NONE);
mapheaderinfo[num]->menuflags = 0;
// Flickies. Nope, no delfile support here either
#if 1 // equivalent to "FlickyList = DEMO"
P_SetDemoFlickies(num);
#else // equivalent to "FlickyList = NONE"
P_DeleteFlickies(num);
#endif
// TODO grades support for delfile (pfft yeah right)
P_DeleteGrades(num);
// an even further impossibility, delfile custom opts support
@ -241,6 +274,7 @@ void P_AllocMapHeader(INT16 i)
if (!mapheaderinfo[i])
{
mapheaderinfo[i] = Z_Malloc(sizeof(mapheader_t), PU_STATIC, NULL);
mapheaderinfo[i]->flickies = NULL;
mapheaderinfo[i]->grades = NULL;
}
P_ClearSingleMapHeaderInfo(i + 1);
@ -574,6 +608,69 @@ INT32 P_AddLevelFlat(const char *flatname, levelflat_t *levelflat)
return (INT32)i;
}
// help function for Lua and $$$.sav reading
// same as P_AddLevelFlat, except this is not setup so we must realloc levelflats to fit in the new flat
// no longer a static func in lua_maplib.c because p_saveg.c also needs it
//
INT32 P_AddLevelFlatRuntime(const char *flatname)
{
size_t i;
levelflat_t *levelflat = levelflats;
//
// first scan through the already found flats
//
for (i = 0; i < numlevelflats; i++, levelflat++)
if (strnicmp(levelflat->name,flatname,8)==0)
break;
// that flat was already found in the level, return the id
if (i == numlevelflats)
{
// allocate new flat memory
levelflats = Z_Realloc(levelflats, (numlevelflats + 1) * sizeof(*levelflats), PU_LEVEL, NULL);
levelflat = levelflats+i;
// store the name
strlcpy(levelflat->name, flatname, sizeof (levelflat->name));
strupr(levelflat->name);
// store the flat lump number
levelflat->lumpnum = R_GetFlatNumForName(flatname);
#ifndef ZDEBUG
CONS_Debug(DBG_SETUP, "flat #%03d: %s\n", atoi(sizeu1(numlevelflats)), levelflat->name);
#endif
numlevelflats++;
}
// level flat id
return (INT32)i;
}
// help function for $$$.sav checking
// this simply returns the flat # for the name given
//
INT32 P_CheckLevelFlat(const char *flatname)
{
size_t i;
levelflat_t *levelflat = levelflats;
//
// scan through the already found flats
//
for (i = 0; i < numlevelflats; i++, levelflat++)
if (strnicmp(levelflat->name,flatname,8)==0)
break;
if (i == numlevelflats)
return 0; // ??? flat was not found, this should not happen!
// level flat id
return (INT32)i;
}
static void P_LoadSectors(lumpnum_t lumpnum)
{
UINT8 *data;
@ -614,6 +711,7 @@ static void P_LoadSectors(lumpnum_t lumpnum)
ss->special = SHORT(ms->special);
ss->tag = SHORT(ms->tag);
ss->nexttag = ss->firsttag = -1;
ss->spawn_nexttag = ss->spawn_firsttag = -1;
memset(&ss->soundorg, 0, sizeof(ss->soundorg));
ss->validcount = 0;

View file

@ -47,6 +47,8 @@ typedef struct
extern size_t numlevelflats;
extern levelflat_t *levelflats;
INT32 P_AddLevelFlat(const char *flatname, levelflat_t *levelflat);
INT32 P_AddLevelFlatRuntime(const char *flatname);
INT32 P_CheckLevelFlat(const char *flatname);
extern size_t nummapthings;
extern mapthing_t *mapthings;
@ -66,6 +68,9 @@ void P_WriteThings(lumpnum_t lump);
size_t P_PrecacheLevelFlats(void);
void P_AllocMapHeader(INT16 i);
void P_SetDemoFlickies(INT16 i);
void P_DeleteFlickies(INT16 i);
// Needed for NiGHTS
void P_ReloadRings(void);
void P_DeleteGrades(INT16 i);

View file

@ -1519,6 +1519,8 @@ static inline void P_InitTagLists(void)
size_t j = (unsigned)sectors[i].tag % numsectors;
sectors[i].nexttag = sectors[j].firsttag;
sectors[j].firsttag = (INT32)i;
sectors[i].spawn_nexttag = sectors[i].nexttag;
sectors[j].spawn_firsttag = sectors[j].firsttag;
}
for (i = numlines - 1; i != (size_t)-1; i--)
@ -3581,7 +3583,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
P_PlayerFlagBurst(player, false);
break;
case 12: // Space Countdown
if ((player->powers[pw_shield] & SH_NOSTACK) != SH_ELEMENTAL && !player->powers[pw_spacetime])
if (!(player->powers[pw_shield] & SH_PROTECTWATER) && !player->powers[pw_spacetime])
player->powers[pw_spacetime] = spacetimetics + 1;
break;
case 13: // Ramp Sector (Increase step-up/down)
@ -5366,6 +5368,10 @@ void T_LaserFlash(laserthink_t *flash)
&& thing->flags & MF_BOSS)
continue; // Don't hurt bosses
// Don't endlessly kill egg guard shields (or anything else for that matter)
if (thing->health <= 0)
continue;
top = P_GetSpecialTopZ(thing, sourcesec, sector);
bottom = P_GetSpecialBottomZ(thing, sourcesec, sector);

View file

@ -650,7 +650,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
if (!(player->pflags & PF_NIGHTSMODE))
player->mo->height = P_GetPlayerHeight(player); // Just to make sure jumping into the drone doesn't result in a squashed hitbox.
player->pflags &= ~(PF_USEDOWN|PF_JUMPDOWN|PF_ATTACKDOWN|PF_STARTDASH|PF_GLIDING|PF_JUMPED|PF_THOKKED|PF_SPINNING|PF_DRILLING);
player->pflags &= ~(PF_USEDOWN|PF_JUMPDOWN|PF_ATTACKDOWN|PF_STARTDASH|PF_GLIDING|PF_JUMPED|PF_THOKKED|PF_SHIELDABILITY|PF_SPINNING|PF_DRILLING);
player->homing = 0;
player->mo->fuse = 0;
player->speed = 0;
@ -790,59 +790,64 @@ boolean P_PlayerInPain(player_t *player)
//
void P_DoPlayerPain(player_t *player, mobj_t *source, mobj_t *inflictor)
{
angle_t ang;
fixed_t fallbackspeed;
if (player->mo->eflags & MFE_VERTICALFLIP)
player->mo->z--;
else
player->mo->z++;
if (player->mo->eflags & MFE_UNDERWATER)
P_SetObjectMomZ(player->mo, FixedDiv(10511*FRACUNIT,2600*FRACUNIT), false);
else
P_SetObjectMomZ(player->mo, FixedDiv(69*FRACUNIT,10*FRACUNIT), false);
if (inflictor)
{
ang = R_PointToAngle2(inflictor->x-inflictor->momx, inflictor->y - inflictor->momy, player->mo->x - player->mo->momx, player->mo->y - player->mo->momy);
// explosion and rail rings send you farther back, making it more difficult
// to recover
if ((inflictor->flags2 & MF2_SCATTER) && source)
{
fixed_t dist = P_AproxDistance(P_AproxDistance(source->x-player->mo->x, source->y-player->mo->y), source->z-player->mo->z);
dist = FixedMul(128*FRACUNIT, inflictor->scale) - dist/4;
if (dist < FixedMul(4*FRACUNIT, inflictor->scale))
dist = FixedMul(4*FRACUNIT, inflictor->scale);
fallbackspeed = dist;
}
else if (inflictor->flags2 & MF2_EXPLOSION)
{
if (inflictor->flags2 & MF2_RAILRING)
fallbackspeed = FixedMul(38*FRACUNIT, inflictor->scale); // 7x
else
fallbackspeed = FixedMul(30*FRACUNIT, inflictor->scale); // 5x
}
else if (inflictor->flags2 & MF2_RAILRING)
fallbackspeed = FixedMul(45*FRACUNIT, inflictor->scale); // 4x
else
fallbackspeed = FixedMul(4*FRACUNIT, inflictor->scale); // the usual amount of force
}
else
{
ang = R_PointToAngle2(player->mo->momx, player->mo->momy, 0, 0);
fallbackspeed = FixedMul(4*FRACUNIT, player->mo->scale);
}
P_InstaThrust(player->mo, ang, fallbackspeed);
if (player->powers[pw_carry] == CR_ROPEHANG)
P_SetTarget(&player->mo->tracer, NULL);
{
angle_t ang;
fixed_t fallbackspeed;
P_ResetPlayer(player);
P_SetPlayerMobjState(player->mo, player->mo->info->painstate);
if (player->mo->eflags & MFE_VERTICALFLIP)
player->mo->z--;
else
player->mo->z++;
if (player->mo->eflags & MFE_UNDERWATER)
P_SetObjectMomZ(player->mo, FixedDiv(10511*FRACUNIT,2600*FRACUNIT), false);
else
P_SetObjectMomZ(player->mo, FixedDiv(69*FRACUNIT,10*FRACUNIT), false);
if (inflictor)
{
ang = R_PointToAngle2(inflictor->x-inflictor->momx, inflictor->y - inflictor->momy, player->mo->x - player->mo->momx, player->mo->y - player->mo->momy);
// explosion and rail rings send you farther back, making it more difficult
// to recover
if ((inflictor->flags2 & MF2_SCATTER) && source)
{
fixed_t dist = P_AproxDistance(P_AproxDistance(source->x-player->mo->x, source->y-player->mo->y), source->z-player->mo->z);
dist = FixedMul(128*FRACUNIT, inflictor->scale) - dist/4;
if (dist < FixedMul(4*FRACUNIT, inflictor->scale))
dist = FixedMul(4*FRACUNIT, inflictor->scale);
fallbackspeed = dist;
}
else if (inflictor->flags2 & MF2_EXPLOSION)
{
if (inflictor->flags2 & MF2_RAILRING)
fallbackspeed = FixedMul(38*FRACUNIT, inflictor->scale); // 7x
else
fallbackspeed = FixedMul(30*FRACUNIT, inflictor->scale); // 5x
}
else if (inflictor->flags2 & MF2_RAILRING)
fallbackspeed = FixedMul(45*FRACUNIT, inflictor->scale); // 4x
else
fallbackspeed = FixedMul(4*FRACUNIT, inflictor->scale); // the usual amount of force
}
else
{
ang = R_PointToAngle2(player->mo->momx, player->mo->momy, 0, 0);
fallbackspeed = FixedMul(4*FRACUNIT, player->mo->scale);
}
P_InstaThrust(player->mo, ang, fallbackspeed);
}
// Point penalty for hitting a hazard during tag.
// Discourages players from intentionally hurting themselves to avoid being tagged.
if (gametype == GT_TAG && (!(player->pflags & PF_TAGGED) && !(player->pflags & PF_TAGIT)))
@ -853,8 +858,6 @@ void P_DoPlayerPain(player_t *player, mobj_t *source, mobj_t *inflictor)
player->score = 0;
}
P_ResetPlayer(player);
P_SetPlayerMobjState(player->mo, player->mo->info->painstate);
player->powers[pw_flashing] = flashingtics;
if (player->timeshit != UINT8_MAX)
@ -867,7 +870,7 @@ void P_DoPlayerPain(player_t *player, mobj_t *source, mobj_t *inflictor)
// Useful when you want to kill everything the player is doing.
void P_ResetPlayer(player_t *player)
{
player->pflags &= ~(PF_SPINNING|PF_STARTDASH|PF_JUMPED|PF_GLIDING|PF_THOKKED|PF_CANCARRY);
player->pflags &= ~(PF_SPINNING|PF_STARTDASH|PF_JUMPED|PF_FORCEJUMPDAMAGE|PF_GLIDING|PF_THOKKED|PF_CANCARRY|PF_SHIELDABILITY);
player->powers[pw_carry] = CR_NONE;
player->jumping = 0;
player->secondjump = 0;
@ -1348,24 +1351,38 @@ void P_SpawnShieldOrb(player_t *player)
I_Error("P_SpawnShieldOrb: player->mo is NULL!\n");
#endif
#ifdef HAVE_BLUA
if (LUAh_ShieldSpawn(player))
return;
#endif
if (player->powers[pw_shield] & SH_FORCE)
orbtype = MT_BLUEORB;
orbtype = MT_FORCE_ORB;
else switch (player->powers[pw_shield] & SH_NOSTACK)
{
case SH_JUMP:
orbtype = MT_WHITEORB;
case SH_WHIRLWIND:
orbtype = MT_WHIRLWIND_ORB;
break;
case SH_ATTRACT:
orbtype = MT_YELLOWORB;
orbtype = MT_ATTRACT_ORB;
break;
case SH_ELEMENTAL:
orbtype = MT_GREENORB;
orbtype = MT_ELEMENTAL_ORB;
break;
case SH_BOMB:
orbtype = MT_BLACKORB;
case SH_ARMAGEDDON:
orbtype = MT_ARMAGEDDON_ORB;
break;
case SH_PITY:
orbtype = MT_PITYORB;
orbtype = MT_PITY_ORB;
break;
case SH_FLAMEAURA:
orbtype = MT_FLAMEAURA_ORB;
break;
case SH_BUBBLEWRAP:
orbtype = MT_BUBBLEWRAP_ORB;
break;
case SH_THUNDERCOIN:
orbtype = MT_THUNDERCOIN_ORB;
break;
default:
return;
@ -1384,14 +1401,17 @@ void P_SpawnShieldOrb(player_t *player)
}
shieldobj = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, orbtype);
shieldobj->flags2 |= MF2_SHIELD;
P_SetTarget(&shieldobj->target, player->mo);
shieldobj->color = (UINT8)shieldobj->info->painchance;
shieldobj->threshold = (player->powers[pw_shield] & SH_FORCE) ? SH_FORCE : (player->powers[pw_shield] & SH_NOSTACK);
if (shieldobj->info->seestate)
{
ov = P_SpawnMobj(shieldobj->x, shieldobj->y, shieldobj->z, MT_OVERLAY);
P_SetTarget(&ov->target, shieldobj);
P_SetMobjState(ov, shieldobj->info->seestate);
P_SetTarget(&shieldobj->tracer, ov);
}
if (shieldobj->info->meleestate)
{
@ -1408,7 +1428,7 @@ void P_SpawnShieldOrb(player_t *player)
if (player->powers[pw_shield] & SH_FORCE)
{
//Copy and pasted from P_ShieldLook in p_mobj.c
shieldobj->movecount = (player->powers[pw_shield] & 0xFF);
shieldobj->movecount = (player->powers[pw_shield] & SH_FORCEHP);
if (shieldobj->movecount < 1)
{
if (shieldobj->info->painstate)
@ -1419,6 +1439,51 @@ void P_SpawnShieldOrb(player_t *player)
}
}
//
// P_SwitchShield
//
// Handles the possibility of switching between
// the non-stack layer of shields thoroughly,
// then adds the desired one.
//
void P_SwitchShield(player_t *player, UINT16 shieldtype)
{
boolean donthavealready = (shieldtype & SH_FORCE)
? (!(player->powers[pw_shield] & SH_FORCE) || (player->powers[pw_shield] & SH_FORCEHP) < (shieldtype & ~SH_FORCE))
: ((player->powers[pw_shield] & SH_NOSTACK) != shieldtype);
if (donthavealready)
{
boolean stopshieldability = (shieldtype & SH_FORCE)
? (!(player->powers[pw_shield] & SH_FORCE))
: true;
// Just in case.
if (stopshieldability && player->pflags & PF_SHIELDABILITY)
{
player->pflags &= ~(PF_SPINNING|PF_SHIELDABILITY); // They'll still have PF_THOKKED...
player->homing = 0;
}
player->powers[pw_shield] = shieldtype|(player->powers[pw_shield] & SH_STACK);
P_SpawnShieldOrb(player);
if (shieldtype & SH_PROTECTWATER)
{
if (player->powers[pw_underwater] && player->powers[pw_underwater] <= 12*TICRATE + 1)
P_RestoreMusic(player);
player->powers[pw_underwater] = 0;
if (player->powers[pw_spacetime] > 1)
{
player->powers[pw_spacetime] = 0;
P_RestoreMusic(player);
}
}
}
}
//
// P_SpawnGhostMobj
//
@ -2120,7 +2185,7 @@ static void P_CheckInvincibilityTimer(player_t *player)
{
if (mariomode)
{
if (player->powers[pw_shield] & SH_FIREFLOWER)
if ((player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER)
{
player->mo->color = SKINCOLOR_WHITE;
G_GhostAddColor(GHC_FIREFLOWER);
@ -2157,7 +2222,7 @@ static void P_DoBubbleBreath(player_t *player)
fixed_t z = player->mo->z;
mobj_t *bubble = NULL;
if (!(player->mo->eflags & MFE_UNDERWATER) || ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL && !(player->pflags & PF_NIGHTSMODE)) || player->spectator)
if (!(player->mo->eflags & MFE_UNDERWATER) || ((player->powers[pw_shield] & SH_PROTECTWATER) && !(player->pflags & PF_NIGHTSMODE)) || player->spectator)
return;
if (player->charflags & SF_MACHINE)
@ -3143,7 +3208,7 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd)
if (cmd->buttons & BT_ATTACK || cmd->buttons & BT_FIRENORMAL)
{
if (!(player->pflags & PF_ATTACKDOWN) && player->powers[pw_shield] & SH_FIREFLOWER && !player->climbing)
if (!(player->pflags & PF_ATTACKDOWN) && (player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER && !player->climbing)
{
player->pflags |= PF_ATTACKDOWN;
P_SpawnPlayerMissile(player->mo, MT_FIREBALL, 0);
@ -3359,7 +3424,7 @@ static void P_DoSuperStuff(player_t *player)
P_SpawnShieldOrb(player);
// Restore color
if (player->powers[pw_shield] & SH_FIREFLOWER)
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_FIREFLOWER)
{
player->mo->color = SKINCOLOR_WHITE;
G_GhostAddColor(GHC_FIREFLOWER);
@ -3406,7 +3471,7 @@ static void P_DoSuperStuff(player_t *player)
player->powers[pw_super] = 0;
// Restore color
if (player->powers[pw_shield] & SH_FIREFLOWER)
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_FIREFLOWER)
{
player->mo->color = SKINCOLOR_WHITE;
G_GhostAddColor(GHC_FIREFLOWER);
@ -3666,6 +3731,35 @@ void P_DoJump(player_t *player, boolean soundandstate)
}
}
static void P_DoSpinDashDust(player_t *player)
{
UINT32 i;
mobj_t *particle;
INT32 prandom[3];
for (i = 0; i <= (leveltime%7)/2; i++) { // 1, 2, 3 or 4 particles
particle = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_SPINDUST);
if (player->mo->eflags & (MFE_TOUCHWATER|MFE_UNDERWATER)) // overrides fire version
P_SetMobjState(particle, S_SPINDUST_BUBBLE1);
else if (player->powers[pw_shield] == SH_ELEMENTAL)
P_SetMobjState(particle, S_SPINDUST_FIRE1);
P_SetTarget(&particle->target, player->mo);
particle->destscale = (2*player->mo->scale)/3;
P_SetScale(particle, particle->destscale);
if (player->mo->eflags & MFE_VERTICALFLIP) // readjust z position if needed
particle->z = player->mo->z + player->mo->height - particle->height;
prandom[0] = P_RandomFixed()<<2; // P_RandomByte()<<10
prandom[1] = P_RandomRange(-30, 30); // P_RandomRange(-ANG30/FRACUNIT, ANG30/FRACUNIT)*FRACUNIT
prandom[2] = P_RandomFixed()<<3; // P_RandomByte()<<11
P_SetObjectMomZ(particle, player->dashspeed/50 + prandom[0], false);
P_InstaThrust(particle,
player->mo->angle + (prandom[1]*ANG1),
-FixedMul(player->dashspeed/12 + FRACUNIT + prandom[2], player->mo->scale));
P_TryMove(particle, particle->x+particle->momx, particle->y+particle->momy, true);
}
}
//
// P_DoSpinAbility
//
@ -3673,6 +3767,7 @@ void P_DoJump(player_t *player, boolean soundandstate)
//
static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
{
boolean canstand = true; // can we stand on the ground? (mostly relevant for slopes)
if (player->pflags & PF_STASIS)
return;
@ -3684,69 +3779,92 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
}
#endif
// Spinning and Spindashing
if ((player->charability2 == CA2_SPINDASH) && !(player->pflags & PF_SLIDING) && !player->exiting
&& !P_PlayerInPain(player)) // subsequent revs
{
if ((cmd->buttons & BT_USE) && player->speed < FixedMul(5<<FRACBITS, player->mo->scale) && !player->mo->momz && onground && !(player->pflags & PF_USEDOWN) && !(player->pflags & PF_SPINNING)
#ifdef ESLOPE
&& (!player->mo->standingslope || (player->mo->standingslope->flags & SL_NOPHYSICS) || abs(player->mo->standingslope->zdelta) < FRACUNIT/2)
canstand = (!player->mo->standingslope || (player->mo->standingslope->flags & SL_NOPHYSICS) || abs(player->mo->standingslope->zdelta) < FRACUNIT/2);
#endif
)
{
player->mo->momx = player->cmomx;
player->mo->momy = player->cmomy;
player->pflags |= PF_STARTDASH|PF_SPINNING;
player->dashspeed = player->mindash;
P_SetPlayerMobjState(player->mo, S_PLAY_DASH);
player->pflags |= PF_USEDOWN;
if (!player->spectator)
S_StartSound(player->mo, sfx_s3kab); // Make the rev sound! Previously sfx_spndsh.
}
else if ((cmd->buttons & BT_USE) && (player->pflags & PF_STARTDASH))
{
if (player->dashspeed < player->maxdash)
{
#define chargecalculation (6*(player->dashspeed - player->mindash))/(player->maxdash - player->mindash)
fixed_t soundcalculation = chargecalculation;
player->dashspeed += FRACUNIT;
if (!player->spectator && soundcalculation != chargecalculation)
S_StartSound(player->mo, sfx_s3kab); // Make the rev sound! Previously sfx_spndsh.
#undef chargecalculation
}
if (player->revitem && !(leveltime % 5)) // Now spawn the color thok circle.
{
P_SpawnSpinMobj(player, player->revitem);
if (demorecording)
G_GhostAddRev();
}
}
// If not moving up or down, and travelling faster than a speed of four while not holding
// down the spin button and not spinning.
// AKA Just go into a spin on the ground, you idiot. ;)
else if ((cmd->buttons & BT_USE || ((twodlevel || (player->mo->flags2 & MF2_TWOD)) && cmd->forwardmove < -20))
&& !player->climbing && !player->mo->momz && onground && (player->speed > FixedMul(5<<FRACBITS, player->mo->scale)
#ifdef ESLOPE
|| (player->mo->standingslope && (!(player->mo->standingslope->flags & SL_NOPHYSICS)) && abs(player->mo->standingslope->zdelta) >= FRACUNIT/2)
#endif
) && !(player->pflags & PF_USEDOWN) && !(player->pflags & PF_SPINNING))
///////////////////////////////
// ability-specific behavior //
///////////////////////////////
if (!(player->pflags & PF_SLIDING) && !player->exiting && !P_PlayerInPain(player))
{
switch (player->charability2)
{
player->pflags |= PF_SPINNING;
P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
if (!player->spectator)
S_StartSound(player->mo, sfx_spin);
player->pflags |= PF_USEDOWN;
case CA2_SPINDASH: // Spinning and Spindashing
// Start revving
if ((cmd->buttons & BT_USE) && player->speed < FixedMul(5<<FRACBITS, player->mo->scale)
&& !player->mo->momz && onground && !(player->pflags & (PF_USEDOWN|PF_SPINNING))
&& canstand)
{
player->mo->momx = player->cmomx;
player->mo->momy = player->cmomy;
player->pflags |= PF_STARTDASH|PF_SPINNING;
player->dashspeed = player->mindash;
P_SetPlayerMobjState(player->mo, S_PLAY_DASH);
player->pflags |= PF_USEDOWN;
if (!player->spectator)
S_StartSound(player->mo, sfx_s3kab); // Make the rev sound! Previously sfx_spndsh.
}
// Revving
else if ((cmd->buttons & BT_USE) && (player->pflags & PF_STARTDASH))
{
if (player->dashspeed < player->maxdash)
{
#define chargecalculation (6*(player->dashspeed - player->mindash))/(player->maxdash - player->mindash)
fixed_t soundcalculation = chargecalculation;
player->dashspeed += FRACUNIT;
if (!player->spectator && soundcalculation != chargecalculation)
S_StartSound(player->mo, sfx_s3kab); // Make the rev sound! Previously sfx_spndsh.
#undef chargecalculation
}
if (player->revitem && !(leveltime % 5)) // Now spawn the color thok circle.
{
P_SpawnSpinMobj(player, player->revitem);
if (demorecording)
G_GhostAddRev();
}
}
// If not moving up or down, and travelling faster than a speed of four while not holding
// down the spin button and not spinning.
// AKA Just go into a spin on the ground, you idiot. ;)
else if ((cmd->buttons & BT_USE || ((twodlevel || (player->mo->flags2 & MF2_TWOD)) && cmd->forwardmove < -20))
&& !player->climbing && !player->mo->momz && onground && (player->speed > FixedMul(5<<FRACBITS, player->mo->scale)
|| !canstand) && !(player->pflags & (PF_USEDOWN|PF_SPINNING)))
{
player->pflags |= PF_SPINNING;
P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
if (!player->spectator)
S_StartSound(player->mo, sfx_spin);
player->pflags |= PF_USEDOWN;
}
break;
case CA2_MELEE: // Melee attack
if (!(player->panim == PA_ABILITY2) && (cmd->buttons & BT_USE) && player->speed < FixedMul(10<<FRACBITS, player->mo->scale)
&& !player->mo->momz && onground && !(player->pflags & PF_USEDOWN)
&& canstand)
{
P_ResetPlayer(player);
player->mo->z += P_MobjFlip(player->mo);
player->mo->momx = player->cmomx = 0;
player->mo->momy = player->cmomy = 0;
P_SetObjectMomZ(player->mo, player->mindash, false);
P_InstaThrust(player->mo, player->mo->angle, FixedMul(player->maxdash, player->mo->scale));
P_SetPlayerMobjState(player->mo, S_PLAY_MELEE);
player->pflags |= PF_USEDOWN;
S_StartSound(player->mo, sfx_s3k8b);
}
break;
}
}
///////////////////////////////
// general spinning behavior //
///////////////////////////////
// Rolling normally
if (onground && player->pflags & PF_SPINNING && !(player->pflags & PF_STARTDASH)
&& player->speed < FixedMul(5*FRACUNIT,player->mo->scale)
#ifdef ESLOPE
&& (!player->mo->standingslope || (player->mo->standingslope->flags & SL_NOPHYSICS) || abs(player->mo->standingslope->zdelta) < FRACUNIT/2)
#endif
)
&& player->speed < FixedMul(5*FRACUNIT,player->mo->scale) && canstand)
{
if (GETSECSPECIAL(player->mo->subsector->sector->special, 4) == 7 || (player->mo->ceilingz - player->mo->floorz < P_GetPlayerHeight(player)))
P_InstaThrust(player->mo, player->mo->angle, FixedMul(10*FRACUNIT, player->mo->scale));
@ -3791,29 +3909,12 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
{
if (player->mo->state-states != S_PLAY_DASH)
P_SetPlayerMobjState(player->mo, S_PLAY_DASH);
// Spawn spin dash dust
if (!(player->charflags & SF_NOSPINDASHDUST) && !(player->mo->eflags & MFE_GOOWATER))
P_DoSpinDashDust(player);
}
else if (onground && player->pflags & PF_SPINNING && !(player->panim == PA_ROLL))
P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
// Melee attack
if ((player->charability2 == CA2_MELEE) && !(player->panim == PA_ABILITY2) && !player->exiting
&& !P_PlayerInPain(player) && (cmd->buttons & BT_USE) && player->speed < FixedMul(10<<FRACBITS, player->mo->scale)
&& !player->mo->momz && onground && !(player->pflags & PF_USEDOWN)
#ifdef ESLOPE
&& (!player->mo->standingslope || (player->mo->standingslope->flags & SL_NOPHYSICS) || abs(player->mo->standingslope->zdelta) < FRACUNIT/2)
#endif
)
{
P_ResetPlayer(player);
player->mo->z += P_MobjFlip(player->mo);
player->mo->momx = player->cmomx = 0;
player->mo->momy = player->cmomy = 0;
P_SetObjectMomZ(player->mo, player->mindash, false);
P_InstaThrust(player->mo, player->mo->angle, FixedMul(player->maxdash, player->mo->scale));
P_SetPlayerMobjState(player->mo, S_PLAY_MELEE);
player->pflags |= PF_USEDOWN;
S_StartSound(player->mo, sfx_s3k8b);
}
}
//
@ -3823,18 +3924,59 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
//
void P_DoJumpShield(player_t *player)
{
boolean electric = ((player->powers[pw_shield] & SH_PROTECTELECTRIC) == SH_PROTECTELECTRIC);
if (player->pflags & PF_THOKKED)
return;
player->pflags &= ~PF_JUMPED;
P_DoJump(player, false);
player->pflags &= ~PF_JUMPED;
player->secondjump = 0;
player->jumping = 0;
player->pflags |= PF_THOKKED;
player->secondjump = 0;
player->pflags |= PF_THOKKED|PF_SHIELDABILITY;
player->pflags &= ~PF_SPINNING;
P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
S_StartSound(player->mo, sfx_wdjump);
if (electric)
{
mobj_t *spark;
INT32 i;
#define numangles 6
#define limitangle (360/numangles)
angle_t travelangle = player->mo->angle + P_RandomRange(-limitangle, limitangle)*ANG1;
for (i = 0; i < numangles; i++)
{
spark = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_THUNDERCOIN_SPARK);
P_InstaThrust(spark, travelangle + i*(ANGLE_MAX/numangles), FixedMul(4*FRACUNIT, spark->scale));
if (i % 2)
P_SetObjectMomZ(spark, -4*FRACUNIT, false);
}
#undef limitangle
#undef numangles
S_StartSound(player->mo, sfx_s3k45);
}
else
{
player->pflags &= ~PF_JUMPED;
P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
S_StartSound(player->mo, sfx_wdjump);
}
}
//
// P_DoBubbleBounce
//
// Bubblewrap shield landing handling
//
void P_DoBubbleBounce(player_t *player)
{
player->pflags &= ~(PF_JUMPED|PF_SHIELDABILITY);
S_StartSound(player->mo, sfx_s3k44);
P_DoJump(player, false);
if (player->charflags & SF_NOJUMPSPIN)
P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
player->pflags |= PF_THOKKED;
player->jumping = 0;
player->secondjump = UINT8_MAX;
player->mo->momz = FixedMul(player->mo->momz, 5*FRACUNIT/4);
}
//
@ -3905,6 +4047,10 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
{}
else if (onground || player->climbing || (player->mo->tracer && player->powers[pw_carry]))
{}
else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_WHIRLWIND
&& !(player->pflags & PF_JUMPED)
&& !(player->pflags & PF_USEDOWN))
P_DoJumpShield(player);
else if (!(player->pflags & PF_SLIDING) && ((gametype != GT_CTF) || (!player->gotflag)))
{
#ifdef HAVE_BLUA
@ -4047,7 +4193,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
if (player->charability == CA_HOMINGTHOK && !player->homing)
{
if (P_LookForEnemies(player))
if (P_LookForEnemies(player, true))
{
if (player->mo->tracer)
player->homing = 3*TICRATE;
@ -4175,7 +4321,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
break;
}
}
else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_JUMP && !player->powers[pw_super])
else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_WHIRLWIND && !player->powers[pw_super])
P_DoJumpShield(player);
}
@ -5494,11 +5640,13 @@ static void P_DoNiGHTSCapsule(player_t *player)
}
else
{
fixed_t z;
z = player->capsule->z + player->capsule->height/2;
for (i = 0; i < 16; i++)
P_SpawnMobj(player->capsule->x, player->capsule->y, z, MT_BIRD);
{
mobj_t *flicky = P_InternalFlickySpawn(player->capsule, 0, ((i%4) + 1)*2*FRACUNIT, true);
flicky->z += player->capsule->height/2;
flicky->angle = (i*(ANGLE_MAX/16));
P_InstaThrust(flicky, flicky->angle, 8*FRACUNIT);
}
}
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && players[i].mare == player->mare)
@ -6143,7 +6291,7 @@ void P_BlackOw(player_t *player)
player->powers[pw_shield] = player->powers[pw_shield] & SH_STACK;
}
void P_ElementalFireTrail(player_t *player)
void P_ElementalFire(player_t *player, boolean cropcircle)
{
fixed_t newx;
fixed_t newy;
@ -6161,40 +6309,65 @@ void P_ElementalFireTrail(player_t *player)
else
ground = player->mo->floorz;
travelangle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy);
for (i = 0; i < 2; i++)
if (cropcircle)
{
newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(24*FRACUNIT, player->mo->scale));
newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(24*FRACUNIT, player->mo->scale));
#ifdef ESLOPE
if (player->mo->standingslope)
#define numangles 8
#define limitangle (180/numangles)
travelangle = player->mo->angle + P_RandomRange(-limitangle, limitangle)*ANG1;
for (i = 0; i < numangles; i++)
{
ground = P_GetZAt(player->mo->standingslope, newx, newy);
if (player->mo->eflags & MFE_VERTICALFLIP)
ground -= FixedMul(mobjinfo[MT_SPINFIRE].height, player->mo->scale);
flame = P_SpawnMobj(player->mo->x, player->mo->y, ground, MT_SPINFIRE);
flame->flags &= ~MF_NOGRAVITY;
P_SetTarget(&flame->target, player->mo);
flame->angle = travelangle + i*(ANGLE_MAX/numangles);
flame->fuse = TICRATE*7; // takes about an extra second to hit the ground
flame->destscale = player->mo->scale;
P_SetScale(flame, player->mo->scale);
flame->eflags = (flame->eflags & ~MFE_VERTICALFLIP)|(player->mo->eflags & MFE_VERTICALFLIP);
P_InstaThrust(flame, flame->angle, FixedMul(3*FRACUNIT, flame->scale));
P_SetObjectMomZ(flame, 3*FRACUNIT, false);
}
#endif
flame = P_SpawnMobj(newx, newy, ground, MT_SPINFIRE);
P_SetTarget(&flame->target, player->mo);
flame->angle = travelangle;
flame->fuse = TICRATE*6;
flame->destscale = player->mo->scale;
P_SetScale(flame, player->mo->scale);
flame->eflags = (flame->eflags & ~MFE_VERTICALFLIP)|(player->mo->eflags & MFE_VERTICALFLIP);
flame->momx = 8;
P_XYMovement(flame);
if (P_MobjWasRemoved(flame))
continue;
if (player->mo->eflags & MFE_VERTICALFLIP)
#undef limitangle
#undef numangles
}
else
{
travelangle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy);
for (i = 0; i < 2; i++)
{
if (flame->z + flame->height < flame->ceilingz)
newx = player->mo->x + P_ReturnThrustX(player->mo, (travelangle + ((i&1) ? -1 : 1)*ANGLE_135), FixedMul(24*FRACUNIT, player->mo->scale));
newy = player->mo->y + P_ReturnThrustY(player->mo, (travelangle + ((i&1) ? -1 : 1)*ANGLE_135), FixedMul(24*FRACUNIT, player->mo->scale));
#ifdef ESLOPE
if (player->mo->standingslope)
{
ground = P_GetZAt(player->mo->standingslope, newx, newy);
if (player->mo->eflags & MFE_VERTICALFLIP)
ground -= FixedMul(mobjinfo[MT_SPINFIRE].height, player->mo->scale);
}
#endif
flame = P_SpawnMobj(newx, newy, ground, MT_SPINFIRE);
P_SetTarget(&flame->target, player->mo);
flame->angle = travelangle;
flame->fuse = TICRATE*6;
flame->destscale = player->mo->scale;
P_SetScale(flame, player->mo->scale);
flame->eflags = (flame->eflags & ~MFE_VERTICALFLIP)|(player->mo->eflags & MFE_VERTICALFLIP);
flame->momx = 8; // this is a hack which is used to ensure it still behaves as a missile and can damage others
P_XYMovement(flame);
if (P_MobjWasRemoved(flame))
continue;
if (player->mo->eflags & MFE_VERTICALFLIP)
{
if (flame->z + flame->height < flame->ceilingz)
P_RemoveMobj(flame);
}
else if (flame->z > flame->floorz)
P_RemoveMobj(flame);
}
else if (flame->z > flame->floorz)
P_RemoveMobj(flame);
}
}
@ -6488,7 +6661,7 @@ static void P_MovePlayer(player_t *player)
P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
}
// If Springing (or nojumpspinning), but travelling DOWNWARD, change back! (nojumpspin also turns to fall once PF_THOKKED is added.)
// If Springing (or nojumpspinning), but travelling DOWNWARD, change back!
if ((player->panim == PA_SPRING && P_MobjFlip(player->mo)*player->mo->momz < 0)
|| ((((player->charflags & SF_NOJUMPSPIN) && (player->pflags & PF_JUMPED) && player->panim == PA_JUMP))
&& (P_MobjFlip(player->mo)*player->mo->momz < 0)))
@ -6744,7 +6917,7 @@ static void P_MovePlayer(player_t *player)
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL
&& (player->pflags & PF_SPINNING) && player->speed > FixedMul(4<<FRACBITS, player->mo->scale) && onground && (leveltime & 1)
&& !(player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)))
P_ElementalFireTrail(player);
P_ElementalFire(player, false);
P_DoSpinAbility(player, cmd);
@ -6812,51 +6985,121 @@ static void P_MovePlayer(player_t *player)
localangle2 = player->mo->angle;
}
///////////////////////////
//BOMB SHIELD ACTIVATION,//
//HOMING, AND OTHER COOL //
//STUFF! //
///////////////////////////
//////////////////
//SHIELD ACTIVES//
//& SUPER FLOAT!//
//////////////////
if (cmd->buttons & BT_USE) // Spin button effects
if (player->pflags & PF_JUMPED && !player->exiting && player->mo->health)
{
if (player->pflags & PF_JUMPED) // If the player is jumping
if (cmd->buttons & BT_USE) // Spin button effects
{
if (!(player->pflags & PF_USEDOWN)) // If the player is not holding down BT_USE
if (player->powers[pw_super]) // Super can't use shield actives, only passives
{
// Jump shield activation
if (!P_PlayerInPain(player) // If the player is not in pain
&& !player->climbing // If the player is not climbing
&& !(player->pflags & (PF_GLIDING|PF_SLIDING|PF_THOKKED)) // If the player is not gliding or sliding and hasn't used their ability
&& !onground) // If the player isn't on the ground
if ((player->charability == CA_THOK) // Super Sonic float
&& (player->speed > 5*player->mo->scale) // FixedMul(5<<FRACBITS, player->mo->scale), but scale is FRACUNIT-based
&& (P_MobjFlip(player->mo)*player->mo->momz <= 0))
{
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_JUMP && !player->powers[pw_super])
P_DoJumpShield(player);
}
// Bomb shield activation
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_BOMB)
{
// Don't let Super Sonic or invincibility use it
if (!(player->powers[pw_super] || player->powers[pw_invulnerability]))
P_BlackOw(player);
if (player->panim == PA_PAIN || player->panim == PA_JUMP || player->panim == PA_FALL
|| (player->panim == PA_WALK && player->mo->state-states != S_PLAY_SUPER_FLOAT))
P_SetPlayerMobjState(player->mo, S_PLAY_SUPER_FLOAT);
player->mo->momz = 0;
player->pflags &= ~PF_SPINNING;
player->jumping = 0; // don't cut jump height after bouncing off something
}
}
// Super Sonic move
if (player->skin == 0 && player->powers[pw_super] && player->speed > FixedMul(5<<FRACBITS, player->mo->scale)
&& P_MobjFlip(player->mo)*player->mo->momz <= 0)
else
#ifdef HAVE_BLUA
if (!LUAh_ShieldSpecial(player))
#endif
{
if (player->panim == PA_ROLL || player->panim == PA_JUMP || player->mo->state-states == S_PLAY_PAIN || player->panim == PA_WALK)
P_SetPlayerMobjState(player->mo, S_PLAY_SUPER_FLOAT);
player->mo->momz = 0;
player->pflags &= ~PF_SPINNING;
player->jumping = 0; // don't cut jump height after bouncing off something
if (!(player->pflags & (PF_USEDOWN|PF_GLIDING|PF_SLIDING|PF_SHIELDABILITY)) // If the player is not holding down BT_USE, or having used an ability previously
&& (!(player->pflags & PF_THOKKED) || ((player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP && player->secondjump == UINT8_MAX))) // thokked is optional if you're bubblewrapped
{
// Force shield activation
if ((player->powers[pw_shield] & ~(SH_FORCEHP|SH_STACK)) == SH_FORCE)
{
player->pflags |= PF_THOKKED|PF_SHIELDABILITY;
player->mo->momx = player->mo->momy = player->mo->momz = 0;
S_StartSound(player->mo, sfx_ngskid);
}
else
{
switch (player->powers[pw_shield] & SH_NOSTACK)
{
// Whirlwind/Thundercoin shield activation
case SH_WHIRLWIND:
case SH_THUNDERCOIN:
P_DoJumpShield(player);
break;
// Armageddon shield activation
case SH_ARMAGEDDON:
player->pflags |= PF_THOKKED|PF_SHIELDABILITY;
P_BlackOw(player);
break;
// Attract shield activation
case SH_ATTRACT:
player->pflags |= PF_THOKKED|PF_SHIELDABILITY;
player->homing = 2;
if (P_LookForEnemies(player, false) && player->mo->tracer)
{
player->pflags |= PF_FORCEJUMPDAMAGE;
P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
S_StartSound(player->mo, sfx_s3k40);
player->homing = 3*TICRATE;
}
else
S_StartSound(player->mo, sfx_s3ka6);
break;
// Elemental/Bubblewrap shield activation
case SH_ELEMENTAL:
case SH_BUBBLEWRAP:
player->pflags |= PF_FORCEJUMPDAMAGE|PF_THOKKED|PF_SHIELDABILITY;
P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
player->secondjump = 0;
player->mo->momx = player->mo->momy = 0;
P_SetObjectMomZ(player->mo, -24*FRACUNIT, false);
S_StartSound(player->mo,
((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL)
? sfx_s3k43
: sfx_s3k44);
break;
// Flame shield activation
case SH_FLAMEAURA:
player->pflags |= PF_THOKKED|PF_SHIELDABILITY;
P_Thrust(player->mo, player->mo->angle, FixedMul(30*FRACUNIT - FixedSqrt(FixedDiv(player->speed, player->mo->scale)), player->mo->scale));
S_StartSound(player->mo, sfx_s3k43);
default:
break;
}
}
}
}
}
}
// HOMING option.
if (player->charability == CA_HOMINGTHOK)
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT // Sonic 3D Blast.
&& player->pflags & PF_SHIELDABILITY)
{
if (player->homing && player->mo->tracer)
{
P_HomingAttack(player->mo, player->mo->tracer);
if (player->mo->tracer->health <= 0 || (player->mo->tracer->flags2 & MF2_FRET))
{
P_SetObjectMomZ(player->mo, 6*FRACUNIT, false);
if (player->mo->eflags & MFE_UNDERWATER)
player->mo->momz = FixedMul(player->mo->momz, FRACUNIT/3);
player->homing = 0;
}
}
// If you're not jumping, then you obviously wouldn't be homing.
if (!(player->pflags & PF_JUMPED))
player->homing = 0;
}
else if (player->charability == CA_HOMINGTHOK) // Sonic Adventure.
{
// If you've got a target, chase after it!
if (player->homing && player->mo->tracer)
@ -7159,13 +7402,6 @@ static void P_DoZoomTube(player_t *player)
P_SetTarget(&player->mo->tracer, waypoint);
// calculate MOMX/MOMY/MOMZ for next waypoint
// change angle
player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, player->mo->tracer->x, player->mo->tracer->y);
if (player == &players[consoleplayer])
localangle = player->mo->angle;
else if (player == &players[secondarydisplayplayer])
localangle2 = player->mo->angle;
// change slope
dist = P_AproxDistance(P_AproxDistance(player->mo->tracer->x - player->mo->x, player->mo->tracer->y - player->mo->y), player->mo->tracer->z - player->mo->z);
@ -7185,6 +7421,17 @@ static void P_DoZoomTube(player_t *player)
CONS_Debug(DBG_GAMELOGIC, "Next waypoint not found, releasing from track...\n");
}
}
// change angle
if (player->mo->tracer)
{
player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, player->mo->tracer->x, player->mo->tracer->y);
if (player == &players[consoleplayer])
localangle = player->mo->angle;
else if (player == &players[secondarydisplayplayer])
localangle2 = player->mo->angle;
}
}
//
@ -7440,9 +7687,9 @@ void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius)
//
// P_LookForEnemies
// Looks for something you can hit - Used for homing attack
// Includes monitors and springs!
// If nonenemies is true, includes monitors and springs!
//
boolean P_LookForEnemies(player_t *player)
boolean P_LookForEnemies(player_t *player, boolean nonenemies)
{
mobj_t *mo;
thinker_t *think;
@ -7455,7 +7702,8 @@ boolean P_LookForEnemies(player_t *player)
continue; // not a mobj thinker
mo = (mobj_t *)think;
if (!(mo->flags & (MF_ENEMY|MF_BOSS|MF_MONITOR|MF_SPRING)))
if ((nonenemies && !(mo->flags & (MF_ENEMY|MF_BOSS|MF_MONITOR|MF_SPRING)))
|| (!nonenemies && !(mo->flags & (MF_ENEMY|MF_BOSS))))
continue; // not a valid enemy
if (mo->health <= 0) // dead
@ -7553,7 +7801,12 @@ void P_HomingAttack(mobj_t *source, mobj_t *enemy) // Home in on your target
ns = FixedMul(source->info->speed, source->scale);
}
else if (source->player)
ns = FixedDiv(FixedMul(source->player->actionspd, source->scale), 3*FRACUNIT/2);
{
if (source->player->charability == CA_HOMINGTHOK && !(source->player->pflags & PF_SHIELDABILITY))
ns = FixedDiv(FixedMul(source->player->actionspd, source->scale), 3*FRACUNIT/2);
else
ns = FixedMul(45*FRACUNIT, source->scale);
}
source->momx = FixedMul(FixedDiv(enemy->x - source->x, dist), ns);
source->momy = FixedMul(FixedDiv(enemy->y - source->y, dist), ns);
@ -8667,8 +8920,13 @@ void P_PlayerThink(player_t *player)
if (player->panim != PA_ABILITY)
P_SetPlayerMobjState(player->mo, S_PLAY_GLIDE);
}
else if ((player->pflags & PF_JUMPED) && !player->powers[pw_super] && player->panim != PA_JUMP && !(player->charflags & SF_NOJUMPSPIN))
P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
else if ((player->pflags & PF_JUMPED) && !player->powers[pw_super] && ((player->charflags & SF_NOJUMPSPIN && player->pflags & PF_FORCEJUMPDAMAGE && player->panim != PA_ROLL) || (!(player->charflags & SF_NOJUMPSPIN) && player->panim != PA_JUMP)))
{
if (!(player->charflags & SF_NOJUMPSPIN))
P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
else if (player->pflags & PF_FORCEJUMPDAMAGE)
P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
}
if (player->flashcount)
player->flashcount--;
@ -8987,7 +9245,7 @@ void P_PlayerThink(player_t *player)
if (player->powers[pw_tailsfly] && player->powers[pw_tailsfly] < UINT16_MAX && player->charability != CA_SWIM) // tails fly counter
player->powers[pw_tailsfly]--;
if (player->powers[pw_underwater] && (player->pflags & PF_GODMODE || (player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL))
if (player->powers[pw_underwater] && (player->pflags & PF_GODMODE || (player->powers[pw_shield] & SH_PROTECTWATER)))
{
if (player->powers[pw_underwater] <= 12*TICRATE+1)
P_RestoreMusic(player); //incase they were about to drown
@ -8997,7 +9255,7 @@ void P_PlayerThink(player_t *player)
else if (player->powers[pw_underwater] && !(maptol & TOL_NIGHTS) && !((netgame || multiplayer) && player->spectator)) // underwater timer
player->powers[pw_underwater]--;
if (player->powers[pw_spacetime] && (player->pflags & PF_GODMODE || (player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL))
if (player->powers[pw_spacetime] && (player->pflags & PF_GODMODE || (player->powers[pw_shield] & SH_PROTECTWATER)))
player->powers[pw_spacetime] = 0;
else if (player->powers[pw_spacetime] && !(maptol & TOL_NIGHTS) && !((netgame || multiplayer) && player->spectator)) // underwater timer
player->powers[pw_spacetime]--;
@ -9412,7 +9670,7 @@ void P_PlayerAfterThink(player_t *player)
player->mo->momy = (player->mo->tracer->y - player->mo->y)*2;
player->mo->momz = (player->mo->tracer->z - (player->mo->height-player->mo->tracer->height/2) - player->mo->z)*2;
P_TeleportMove(player->mo, player->mo->tracer->x, player->mo->tracer->y, player->mo->tracer->z - (player->mo->height-player->mo->tracer->height/2));
player->pflags |= PF_JUMPED;
player->pflags |= PF_JUMPED|PF_FORCEJUMPDAMAGE;
player->secondjump = 0;
player->pflags &= ~PF_THOKKED;

View file

@ -859,6 +859,7 @@ static void R_Subsector(size_t num)
static sector_t tempsec; // Deep water hack
extracolormap_t *floorcolormap;
extracolormap_t *ceilingcolormap;
fixed_t floorcenterz, ceilingcenterz;
#ifdef RANGECHECK
if (num >= numsubsectors)
@ -879,6 +880,18 @@ static void R_Subsector(size_t num)
floorcolormap = ceilingcolormap = frontsector->extra_colormap;
floorcenterz =
#ifdef ESLOPE
frontsector->f_slope ? P_GetZAt(frontsector->f_slope, frontsector->soundorg.x, frontsector->soundorg.y) :
#endif
frontsector->floorheight;
ceilingcenterz =
#ifdef ESLOPE
frontsector->c_slope ? P_GetZAt(frontsector->c_slope, frontsector->soundorg.x, frontsector->soundorg.y) :
#endif
frontsector->ceilingheight;
// Check and prep all 3D floors. Set the sector floor/ceiling light levels and colormaps.
if (frontsector->ffloors)
{
@ -891,19 +904,11 @@ static void R_Subsector(size_t num)
sub->sector->moved = frontsector->moved = false;
}
light = R_GetPlaneLight(frontsector,
#ifdef ESLOPE
frontsector->f_slope ? P_GetZAt(frontsector->f_slope, frontsector->soundorg.x, frontsector->soundorg.y) :
#endif
frontsector->floorheight, false);
light = R_GetPlaneLight(frontsector, floorcenterz, false);
if (frontsector->floorlightsec == -1)
floorlightlevel = *frontsector->lightlist[light].lightlevel;
floorcolormap = frontsector->lightlist[light].extra_colormap;
light = R_GetPlaneLight(frontsector,
#ifdef ESLOPE
frontsector->c_slope ? P_GetZAt(frontsector->c_slope, frontsector->soundorg.x, frontsector->soundorg.y) :
#endif
frontsector->ceilingheight, false);
light = R_GetPlaneLight(frontsector, ceilingcenterz, false);
if (frontsector->ceilinglightsec == -1)
ceilinglightlevel = *frontsector->lightlist[light].lightlevel;
ceilingcolormap = frontsector->lightlist[light].extra_colormap;
@ -920,6 +925,9 @@ static void R_Subsector(size_t num)
{
floorplane = R_FindPlane(frontsector->floorheight, frontsector->floorpic, floorlightlevel,
frontsector->floor_xoffs, frontsector->floor_yoffs, frontsector->floorpic_angle, floorcolormap, NULL
#ifdef POLYOBJECTS_PLANES
, NULL
#endif
#ifdef ESLOPE
, frontsector->f_slope
#endif
@ -939,6 +947,9 @@ static void R_Subsector(size_t num)
ceilingplane = R_FindPlane(frontsector->ceilingheight, frontsector->ceilingpic,
ceilinglightlevel, frontsector->ceiling_xoffs, frontsector->ceiling_yoffs, frontsector->ceilingpic_angle,
ceilingcolormap, NULL
#ifdef POLYOBJECTS_PLANES
, NULL
#endif
#ifdef ESLOPE
, frontsector->c_slope
#endif
@ -956,7 +967,7 @@ static void R_Subsector(size_t num)
if (frontsector->ffloors)
{
ffloor_t *rover;
fixed_t heightcheck, planecenterz, floorcenterz, ceilingcenterz;
fixed_t heightcheck, planecenterz;
for (rover = frontsector->ffloors; rover && numffloors < MAXFFLOORS; rover = rover->next)
{
@ -975,18 +986,6 @@ static void R_Subsector(size_t num)
ffloor[numffloors].plane = NULL;
ffloor[numffloors].polyobj = NULL;
floorcenterz =
#ifdef ESLOPE
frontsector->f_slope ? P_GetZAt(frontsector->f_slope, frontsector->soundorg.x, frontsector->soundorg.y) :
#endif
frontsector->floorheight;
ceilingcenterz =
#ifdef ESLOPE
frontsector->c_slope ? P_GetZAt(frontsector->c_slope, frontsector->soundorg.x, frontsector->soundorg.y) :
#endif
frontsector->ceilingheight;
heightcheck =
#ifdef ESLOPE
*rover->b_slope ? P_GetZAt(*rover->b_slope, viewx, viewy) :
@ -1009,6 +1008,9 @@ static void R_Subsector(size_t num)
ffloor[numffloors].plane = R_FindPlane(*rover->bottomheight, *rover->bottompic,
*frontsector->lightlist[light].lightlevel, *rover->bottomxoffs,
*rover->bottomyoffs, *rover->bottomangle, frontsector->lightlist[light].extra_colormap, rover
#ifdef POLYOBJECTS_PLANES
, NULL
#endif
#ifdef ESLOPE
, *rover->b_slope
#endif
@ -1052,6 +1054,9 @@ static void R_Subsector(size_t num)
ffloor[numffloors].plane = R_FindPlane(*rover->topheight, *rover->toppic,
*frontsector->lightlist[light].lightlevel, *rover->topxoffs, *rover->topyoffs, *rover->topangle,
frontsector->lightlist[light].extra_colormap, rover
#ifdef POLYOBJECTS_PLANES
, NULL
#endif
#ifdef ESLOPE
, *rover->t_slope
#endif
@ -1093,8 +1098,8 @@ static void R_Subsector(size_t num)
polysec = po->lines[0]->backsector;
ffloor[numffloors].plane = NULL;
if (polysec->floorheight <= frontsector->ceilingheight
&& polysec->floorheight >= frontsector->floorheight
if (polysec->floorheight <= ceilingcenterz
&& polysec->floorheight >= floorcenterz
&& (viewz < polysec->floorheight))
{
fixed_t xoff, yoff;
@ -1118,11 +1123,13 @@ static void R_Subsector(size_t num)
polysec->floorpic_angle-po->angle,
NULL,
NULL
#ifdef POLYOBJECTS_PLANES
, po
#endif
#ifdef ESLOPE
, NULL // will ffloors be slopable eventually?
#endif
);
//ffloor[numffloors].plane->polyobj = po;
ffloor[numffloors].height = polysec->floorheight;
ffloor[numffloors].polyobj = po;
@ -1139,8 +1146,8 @@ static void R_Subsector(size_t num)
ffloor[numffloors].plane = NULL;
if (polysec->ceilingheight >= frontsector->floorheight
&& polysec->ceilingheight <= frontsector->ceilingheight
if (polysec->ceilingheight >= floorcenterz
&& polysec->ceilingheight <= ceilingcenterz
&& (viewz > polysec->ceilingheight))
{
fixed_t xoff, yoff;
@ -1162,11 +1169,13 @@ static void R_Subsector(size_t num)
ffloor[numffloors].plane = R_FindPlane(polysec->ceilingheight, polysec->ceilingpic,
polysec->lightlevel, xoff, yoff, polysec->ceilingpic_angle-po->angle,
NULL, NULL
#ifdef POLYOBJECTS_PLANES
, po
#endif
#ifdef ESLOPE
, NULL // will ffloors be slopable eventually?
#endif
);
//ffloor[numffloors].plane->polyobj = po;
ffloor[numffloors].polyobj = po;
ffloor[numffloors].height = polysec->ceilingheight;

View file

@ -160,6 +160,7 @@ static inline void R_DrawColumnInCache(column_t *patch, UINT8 *cache, INT32 orig
if (position < 0)
{
count += position;
source -= position; // start further down the column
position = 0;
}
@ -173,6 +174,44 @@ static inline void R_DrawColumnInCache(column_t *patch, UINT8 *cache, INT32 orig
}
}
static inline void R_DrawFlippedColumnInCache(column_t *patch, UINT8 *cache, INT32 originy, INT32 cacheheight, INT32 patchheight)
{
INT32 count, position;
UINT8 *source, *dest;
INT32 topdelta, prevdelta = -1;
while (patch->topdelta != 0xff)
{
topdelta = patch->topdelta;
if (topdelta <= prevdelta)
topdelta += prevdelta;
prevdelta = topdelta;
topdelta = patchheight-patch->length-topdelta;
source = (UINT8 *)patch + 2 + patch->length; // patch + 3 + (patch->length-1)
count = patch->length;
position = originy + topdelta;
if (position < 0)
{
count += position;
source += position; // start further UP the column
position = 0;
}
if (position + count > cacheheight)
count = cacheheight - position;
dest = cache + position;
if (count > 0)
{
for (; dest < cache + position + count; --source)
*dest++ = *source;
}
patch = (column_t *)((UINT8 *)patch + patch->length + 4);
}
}
//
// R_GenerateTexture
//
@ -191,7 +230,7 @@ static UINT8 *R_GenerateTexture(size_t texnum)
texture_t *texture;
texpatch_t *patch;
patch_t *realpatch;
int x, x1, x2, i;
int x, x1, x2, i, width, height;
size_t blocksize;
column_t *patchcol;
UINT32 *colofs;
@ -239,6 +278,7 @@ static UINT8 *R_GenerateTexture(size_t texnum)
if (holey)
{
texture->holes = true;
texture->flip = patch->flip;
blocksize = W_LumpLengthPwad(patch->wad, patch->lump);
block = Z_Calloc(blocksize, PU_STATIC, // will change tag at end of this function
&texturecache[texnum]);
@ -249,6 +289,14 @@ static UINT8 *R_GenerateTexture(size_t texnum)
colofs = (UINT32 *)(void *)(block + 8);
texturecolumnofs[texnum] = colofs;
blocktex = block;
if (patch->flip & 1) // flip the patch horizontally
{
UINT32 *realcolofs = (UINT32 *)realpatch->columnofs;
for (x = 0; x < texture->width; x++)
colofs[x] = realcolofs[texture->width-1-x]; // swap with the offset of the other side of the texture
}
// we can't as easily flip the patch vertically sadly though,
// we have wait until the texture itself is drawn to do that
for (x = 0; x < texture->width; x++)
colofs[x] = LONG(LONG(colofs[x]) + 3);
goto done;
@ -259,6 +307,7 @@ static UINT8 *R_GenerateTexture(size_t texnum)
// multi-patch textures (or 'composite')
texture->holes = false;
texture->flip = 0;
blocksize = (texture->width * 4) + (texture->width * texture->height);
texturememory += blocksize;
block = Z_Malloc(blocksize+1, PU_STATIC, &texturecache[texnum]);
@ -277,7 +326,9 @@ static UINT8 *R_GenerateTexture(size_t texnum)
{
realpatch = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE);
x1 = patch->originx;
x2 = x1 + SHORT(realpatch->width);
width = SHORT(realpatch->width);
height = SHORT(realpatch->height);
x2 = x1 + width;
if (x1 < 0)
x = 0;
@ -289,11 +340,17 @@ static UINT8 *R_GenerateTexture(size_t texnum)
for (; x < x2; x++)
{
patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[x-x1]));
if (patch->flip & 1)
patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[(x1+width-1)-x]));
else
patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[x-x1]));
// generate column ofset lookup
colofs[x] = LONG((x * texture->height) + (texture->width*4));
R_DrawColumnInCache(patchcol, block + LONG(colofs[x]), patch->originy, texture->height);
if (patch->flip & 2)
R_DrawFlippedColumnInCache(patchcol, block + LONG(colofs[x]), patch->originy, texture->height, height);
else
R_DrawColumnInCache(patchcol, block + LONG(colofs[x]), patch->originy, texture->height);
}
}
@ -303,6 +360,32 @@ done:
return blocktex;
}
//
// R_GetTextureNum
//
// Returns the actual texture id that we should use.
// This can either be texnum, the current frame for texnum's anim (if animated),
// or 0 if not valid.
//
INT32 R_GetTextureNum(INT32 texnum)
{
if (texnum < 0 || texnum >= numtextures)
return 0;
return texturetranslation[texnum];
}
//
// R_CheckTextureCache
//
// Use this if you need to make sure the texture is cached before R_GetColumn calls
// e.g.: midtextures and FOF walls
//
void R_CheckTextureCache(INT32 tex)
{
if (!texturecache[tex])
R_GenerateTexture(tex);
}
//
// R_GetColumn
//
@ -469,6 +552,7 @@ void R_LoadTextures(void)
texture->height = SHORT(patchlump->height)*patchcount;
texture->patchcount = patchcount;
texture->holes = false;
texture->flip = 0;
// Allocate information for the texture's patches.
for (k = 0; k < patchcount; k++)
@ -479,6 +563,7 @@ void R_LoadTextures(void)
patch->originy = (INT16)(k*patchlump->height);
patch->wad = (UINT16)w;
patch->lump = texstart + j;
patch->flip = 0;
}
Z_Unlock(patchlump);
@ -502,6 +587,7 @@ static texpatch_t *R_ParsePatch(boolean actuallyLoadPatch)
char *patchName = NULL;
INT16 patchXPos;
INT16 patchYPos;
UINT8 flip = 0;
texpatch_t *resultPatch = NULL;
lumpnum_t patchLumpNum;
@ -598,6 +684,47 @@ static texpatch_t *R_ParsePatch(boolean actuallyLoadPatch)
}
Z_Free(texturesToken);
// Patch parameters block (OPTIONAL)
// added by Monster Iestyn (22/10/16)
// Left Curly Brace
texturesToken = M_GetToken(NULL);
if (texturesToken == NULL)
; // move on and ignore, R_ParseTextures will deal with this
else
{
if (strcmp(texturesToken,"{")==0)
{
Z_Free(texturesToken);
texturesToken = M_GetToken(NULL);
if (texturesToken == NULL)
{
I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s parameters should be",patchName);
}
while (strcmp(texturesToken,"}")!=0)
{
if (stricmp(texturesToken, "FLIPX")==0)
flip |= 1;
else if (stricmp(texturesToken, "FLIPY")==0)
flip |= 2;
Z_Free(texturesToken);
texturesToken = M_GetToken(NULL);
if (texturesToken == NULL)
{
I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s parameters or right curly brace should be",patchName);
}
}
}
else
{
// this is not what we wanted...
// undo last read so R_ParseTextures can re-get the token for its own purposes
M_UnGetToken();
}
Z_Free(texturesToken);
}
if (actuallyLoadPatch == true)
{
// Check lump exists
@ -608,6 +735,7 @@ static texpatch_t *R_ParsePatch(boolean actuallyLoadPatch)
resultPatch->originy = patchYPos;
resultPatch->lump = patchLumpNum & 65535;
resultPatch->wad = patchLumpNum>>16;
resultPatch->flip = flip;
// Clean up a little after ourselves
Z_Free(patchName);
// Then return it

View file

@ -31,6 +31,7 @@ typedef struct
// Block origin (always UL), which has already accounted for the internal origin of the patch.
INT16 originx, originy;
UINT16 wad, lump;
UINT8 flip; // 1 = flipx, 2 = flipy, 3 = both
} texpatch_t;
// A maptexturedef_t describes a rectangular texture,
@ -42,6 +43,7 @@ typedef struct
char name[8];
INT16 width, height;
boolean holes;
UINT8 flip; // 1 = flipx, 2 = flipy, 3 = both
// All the patches[patchcount] are drawn back to front into the cached texture.
INT16 patchcount;
@ -65,6 +67,9 @@ extern CV_PossibleValue_t Color_cons_t[];
void R_LoadTextures(void);
void R_FlushTextureCache(void);
INT32 R_GetTextureNum(INT32 texnum);
void R_CheckTextureCache(INT32 tex);
// Retrieve column data for span blitting.
UINT8 *R_GetColumn(fixed_t tex, INT32 col);

View file

@ -203,6 +203,7 @@ typedef struct r_lightlist_s
fixed_t heightstep;
fixed_t botheight;
fixed_t botheightstep;
fixed_t startheight; // for repeating midtextures
INT16 lightlevel;
extracolormap_t *extra_colormap;
lighttable_t *rcolormap;
@ -224,15 +225,6 @@ typedef struct linechain_s
// ZDoom C++ to Legacy C conversion Tails 04-29-2002 (for slopes)
typedef struct secplane_t
{
// the plane is defined as a*x + b*y + c*z + d = 0
// ic is 1/c, for faster Z calculations
fixed_t a, b, c, d, ic;
} secplane_t;
// Slopes
#ifdef ESLOPE
typedef enum {
@ -392,6 +384,7 @@ typedef struct sector_s
#endif
// these are saved for netgames, so do not let Lua touch these!
INT32 spawn_nexttag, spawn_firsttag; // the actual nexttag/firsttag values may differ if the sector's tag was changed
// offsets sector spawned with (via linedef type 7)
fixed_t spawn_flr_xoffs, spawn_flr_yoffs;

View file

@ -162,6 +162,7 @@ void R_DrawSplat_8(void);
void R_DrawTranslucentSplat_8(void);
void R_DrawTranslucentSpan_8(void);
void R_Draw2sMultiPatchColumn_8(void);
void R_Draw2sMultiPatchTranslucentColumn_8(void);
void R_DrawFogSpan_8(void);
void R_DrawFogColumn_8(void);
void R_DrawColumnShadowed_8(void);

View file

@ -203,6 +203,103 @@ void R_Draw2sMultiPatchColumn_8(void)
}
}
void R_Draw2sMultiPatchTranslucentColumn_8(void)
{
INT32 count;
register UINT8 *dest;
register fixed_t frac;
fixed_t fracstep;
count = dc_yh - dc_yl;
if (count < 0) // Zero length, column does not exceed a pixel.
return;
#ifdef RANGECHECK
if ((unsigned)dc_x >= (unsigned)vid.width || dc_yl < 0 || dc_yh >= vid.height)
return;
#endif
// Framebuffer destination address.
// Use ylookup LUT to avoid multiply with ScreenWidth.
// Use columnofs LUT for subwindows?
//dest = ylookup[dc_yl] + columnofs[dc_x];
dest = &topleft[dc_yl*vid.width + dc_x];
count++;
// Determine scaling, which is the only mapping to be done.
fracstep = dc_iscale;
//frac = dc_texturemid + (dc_yl - centery)*fracstep;
frac = (dc_texturemid + FixedMul((dc_yl << FRACBITS) - centeryfrac, fracstep))*(!dc_hires);
// Inner loop that does the actual texture mapping, e.g. a DDA-like scaling.
// This is as fast as it gets.
{
register const UINT8 *source = dc_source;
register const UINT8 *transmap = dc_transmap;
register const lighttable_t *colormap = dc_colormap;
register INT32 heightmask = dc_texheight-1;
register UINT8 val;
if (dc_texheight & heightmask) // not a power of 2 -- killough
{
heightmask++;
heightmask <<= FRACBITS;
if (frac < 0)
while ((frac += heightmask) < 0);
else
while (frac >= heightmask)
frac -= heightmask;
do
{
// Re-map color indices from wall texture column
// using a lighting/special effects LUT.
// heightmask is the Tutti-Frutti fix
val = source[frac>>FRACBITS];
if (val != TRANSPARENTPIXEL)
*dest = colormap[*(transmap + (val<<8) + (*dest))];
dest += vid.width;
// Avoid overflow.
if (fracstep > 0x7FFFFFFF - frac)
frac += fracstep - heightmask;
else
frac += fracstep;
while (frac >= heightmask)
frac -= heightmask;
} while (--count);
}
else
{
while ((count -= 2) >= 0) // texture height is a power of 2
{
val = source[(frac>>FRACBITS) & heightmask];
if (val != TRANSPARENTPIXEL)
*dest = colormap[*(transmap + (val<<8) + (*dest))];
dest += vid.width;
frac += fracstep;
val = source[(frac>>FRACBITS) & heightmask];
if (val != TRANSPARENTPIXEL)
*dest = colormap[*(transmap + (val<<8) + (*dest))];
dest += vid.width;
frac += fracstep;
}
if (count & 1)
{
val = source[(frac>>FRACBITS) & heightmask];
if (val != TRANSPARENTPIXEL)
*dest = colormap[*(transmap + (val<<8) + (*dest))];
}
}
}
}
/** \brief The R_DrawShadeColumn_8 function
Experiment to make software go faster. Taken from the Boom source
*/

View file

@ -148,7 +148,6 @@ consvar_t cv_flipcam2 = {"flipcam2", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo,
consvar_t cv_shadow = {"shadow", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_shadowoffs = {"offsetshadows", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_skybox = {"skybox", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_soniccd = {"soniccd", "Off", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_allowmlook = {"allowmlook", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_showhud = {"showhud", "Yes", CV_CALL, CV_YesNo, R_SetViewSize, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_translucenthud = {"translucenthud", "10", CV_SAVE, translucenthud_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
@ -366,69 +365,6 @@ fixed_t R_PointToDist(fixed_t x, fixed_t y)
return R_PointToDist2(viewx, viewy, x, y);
}
/***************************************
*** Zdoom C++ to Legacy C conversion ***
****************************************/
// Utility to find the Z height at an XY location in a sector (for slopes)
fixed_t R_SecplaneZatPoint(secplane_t *secplane, fixed_t x, fixed_t y)
{
return FixedMul(secplane->ic, -secplane->d - DMulScale16(secplane->a, x, secplane->b, y));
}
// Returns the value of z at (x,y) if d is equal to dist
fixed_t R_SecplaneZatPointDist (secplane_t *secplane, fixed_t x, fixed_t y, fixed_t dist)
{
return FixedMul(secplane->ic, -dist - DMulScale16(secplane->a, x, secplane->b, y));
}
// Flips the plane's vertical orientiation, so that if it pointed up,
// it will point down, and vice versa.
void R_SecplaneFlipVert(secplane_t *secplane)
{
secplane->a = -secplane->a;
secplane->b = -secplane->b;
secplane->c = -secplane->c;
secplane->d = -secplane->d;
secplane->ic = -secplane->ic;
}
// Returns true if 2 planes are the same
boolean R_ArePlanesSame(secplane_t *original, secplane_t *other)
{
return original->a == other->a && original->b == other->b
&& original->c == other->c && original->d == other->d;
}
// Returns true if 2 planes are different
boolean R_ArePlanesDifferent(secplane_t *original, secplane_t *other)
{
return original->a != other->a || original->b != other->b
|| original->c != other->c || original->d != other->d;
}
// Moves a plane up/down by hdiff units
void R_SecplaneChangeHeight(secplane_t *secplane, fixed_t hdiff)
{
secplane->d = secplane->d - FixedMul(hdiff, secplane->c);
}
// Returns how much this plane's height would change if d were set to oldd
fixed_t R_SecplaneHeightDiff(secplane_t *secplane, fixed_t oldd)
{
return FixedMul(oldd - secplane->d, secplane->ic);
}
fixed_t R_SecplanePointToDist(secplane_t *secplane, fixed_t x, fixed_t y, fixed_t z)
{
return -TMulScale16(secplane->a, x, y, secplane->b, z, secplane->c);
}
fixed_t R_SecplanePointToDist2(secplane_t *secplane, fixed_t x, fixed_t y, fixed_t z)
{
return -TMulScale16(secplane->a, x, secplane->b, y, z, secplane->c);
}
//
// R_ScaleFromGlobalAngle
// Returns the texture mapping scale for the current line (horizontal span)
@ -1424,7 +1360,6 @@ void R_RegisterEngineStuff(void)
{
CV_RegisterVar(&cv_gravity);
CV_RegisterVar(&cv_tailspickup);
CV_RegisterVar(&cv_soniccd);
CV_RegisterVar(&cv_allowmlook);
CV_RegisterVar(&cv_homremoval);
CV_RegisterVar(&cv_flipcam);

View file

@ -61,18 +61,6 @@ angle_t R_PointToAngle2(fixed_t px2, fixed_t py2, fixed_t px1, fixed_t py1);
fixed_t R_PointToDist(fixed_t x, fixed_t y);
fixed_t R_PointToDist2(fixed_t px2, fixed_t py2, fixed_t px1, fixed_t py1);
// ZDoom C++ to Legacy C conversion Tails 04-29-2002
fixed_t R_SecplaneZatPoint(secplane_t *secplane, fixed_t x, fixed_t y);
fixed_t R_SecplaneZatPointDist(secplane_t *secplane, fixed_t x, fixed_t y,
fixed_t dist);
void R_SecplaneFlipVert(secplane_t *secplane);
boolean R_ArePlanesSame(secplane_t *original, secplane_t *other);
boolean R_ArePlanesDifferent(secplane_t *original, secplane_t *other);
void R_SecplaneChangeHeight(secplane_t *secplane, fixed_t hdiff);
fixed_t R_SecplaneHeightDiff(secplane_t *secplane, fixed_t oldd);
fixed_t R_SecplanePointToDist(secplane_t *secplane, fixed_t x, fixed_t y, fixed_t z);
fixed_t R_SecplanePointToDist2(secplane_t *secplane, fixed_t x, fixed_t y, fixed_t z);
fixed_t R_ScaleFromGlobalAngle(angle_t visangle);
subsector_t *R_PointInSubsector(fixed_t x, fixed_t y);
subsector_t *R_IsPointInSubsector(fixed_t x, fixed_t y);

View file

@ -431,6 +431,9 @@ static visplane_t *new_visplane(unsigned hash)
visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel,
fixed_t xoff, fixed_t yoff, angle_t plangle, extracolormap_t *planecolormap,
ffloor_t *pfloor
#ifdef POLYOBJECTS_PLANES
, polyobj_t *polyobj
#endif
#ifdef ESLOPE
, pslope_t *slope
#endif
@ -470,6 +473,8 @@ visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel,
#ifdef POLYOBJECTS_PLANES
if (check->polyobj && pfloor)
continue;
if (polyobj != check->polyobj)
continue;
#endif
if (height == check->height && picnum == check->picnum
&& lightlevel == check->lightlevel
@ -504,7 +509,7 @@ visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel,
check->viewangle = viewangle;
check->plangle = plangle;
#ifdef POLYOBJECTS_PLANES
check->polyobj = NULL;
check->polyobj = polyobj;
#endif
#ifdef ESLOPE
check->slope = slope;
@ -720,7 +725,11 @@ void R_DrawPlanes(void)
continue;
}
if (pl->ffloor != NULL)
if (pl->ffloor != NULL
#ifdef POLYOBJECTS_PLANES
|| pl->polyobj != NULL
#endif
)
continue;
R_DrawSinglePlane(pl);

View file

@ -97,6 +97,9 @@ void R_MakeSpans(INT32 x, INT32 t1, INT32 b1, INT32 t2, INT32 b2);
void R_DrawPlanes(void);
visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, fixed_t xoff, fixed_t yoff, angle_t plangle,
extracolormap_t *planecolormap, ffloor_t *ffloor
#ifdef POLYOBJECTS_PLANES
, polyobj_t *polyobj
#endif
#ifdef ESLOPE
, pslope_t *slope
#endif

View file

@ -271,11 +271,20 @@ static void R_Render2sidedMultiPatchColumn(column_t *column)
if (colfunc == wallcolfunc)
twosmultipatchfunc();
else if (colfunc == fuzzcolfunc)
twosmultipatchtransfunc();
else
colfunc();
}
}
// quick wrapper for R_DrawFlippedMaskedColumn so it can be set as a colfunc_2s value
// uses column2s_length for texture->height as above
static void R_DrawFlippedMaskedSegColumn(column_t *column)
{
R_DrawFlippedMaskedColumn(column, column2s_length);
}
void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
{
size_t pindex;
@ -300,7 +309,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
curline = ds->curline;
frontsector = curline->frontsector;
backsector = curline->backsector;
texnum = texturetranslation[curline->sidedef->midtexture];
texnum = R_GetTextureNum(curline->sidedef->midtexture);
windowbottom = windowtop = sprbotscreen = INT32_MAX;
// hack translucent linedef types (900-909 for transtables 1-9)
@ -344,10 +353,21 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
rw_scalestep = ds->scalestep;
spryscale = ds->scale1 + (x1 - ds->x1)*rw_scalestep;
// Texture must be cached before setting colfunc_2s,
// otherwise texture[texnum]->holes may be false when it shouldn't be
R_CheckTextureCache(texnum);
// handle case where multipatch texture is drawn on a 2sided wall, multi-patch textures
// are not stored per-column with post info in SRB2
if (textures[texnum]->holes)
colfunc_2s = R_DrawMaskedColumn; // render the usual 2sided single-patch packed texture
{
if (textures[texnum]->flip & 2) // vertically flipped?
{
colfunc_2s = R_DrawFlippedMaskedSegColumn;
column2s_length = textures[texnum]->height;
}
else
colfunc_2s = R_DrawMaskedColumn; // render the usual 2sided single-patch packed texture
}
else
{
colfunc_2s = R_Render2sidedMultiPatchColumn; // render multipatch with no holes (no post_t info)
@ -391,6 +411,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
rlight->height = (centeryfrac) - FixedMul((light->height - viewz), spryscale);
rlight->heightstep = -FixedMul(rw_scalestep, (light->height - viewz));
#endif
rlight->startheight = rlight->height; // keep starting value here to reset for each repeat
rlight->lightlevel = *light->lightlevel;
rlight->extra_colormap = light->extra_colormap;
rlight->flags = light->flags;
@ -484,6 +505,14 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
{
rw_scalestep = ds->scalestep;
spryscale = ds->scale1 + (x1 - ds->x1)*rw_scalestep;
if (dc_numlights)
{ // reset all lights to their starting heights
for (i = 0; i < dc_numlights; i++)
{
rlight = &dc_lightlist[i];
rlight->height = rlight->startheight;
}
}
}
#ifndef ESLOPE
@ -700,6 +729,14 @@ static void R_DrawRepeatMaskedColumn(column_t *col)
} while (sprtopscreen < sprbotscreen);
}
static void R_DrawRepeatFlippedMaskedColumn(column_t *col)
{
do {
R_DrawFlippedMaskedColumn(col, column2s_length);
sprtopscreen += dc_texheight*spryscale;
} while (sprtopscreen < sprbotscreen);
}
//
// R_RenderThickSideRange
// Renders all the thick sides in the given range.
@ -746,7 +783,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
curline = ds->curline;
backsector = pfloor->target;
frontsector = curline->frontsector == pfloor->target ? curline->backsector : curline->frontsector;
texnum = texturetranslation[sides[pfloor->master->sidenum[0]].midtexture];
texnum = R_GetTextureNum(sides[pfloor->master->sidenum[0]].midtexture);
colfunc = wallcolfunc;
@ -754,7 +791,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
{
size_t linenum = curline->linedef-backsector->lines[0];
newline = pfloor->master->frontsector->lines[0] + linenum;
texnum = texturetranslation[sides[newline->sidenum[0]].midtexture];
texnum = R_GetTextureNum(sides[newline->sidenum[0]].midtexture);
}
if (pfloor->flags & FF_TRANSLUCENT)
@ -1024,10 +1061,21 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
dc_texturemid += offsetvalue;
// Texture must be cached before setting colfunc_2s,
// otherwise texture[texnum]->holes may be false when it shouldn't be
R_CheckTextureCache(texnum);
//faB: handle case where multipatch texture is drawn on a 2sided wall, multi-patch textures
// are not stored per-column with post info anymore in Doom Legacy
if (textures[texnum]->holes)
colfunc_2s = R_DrawRepeatMaskedColumn; //render the usual 2sided single-patch packed texture
{
if (textures[texnum]->flip & 2) // vertically flipped?
{
colfunc_2s = R_DrawRepeatFlippedMaskedColumn;
column2s_length = textures[texnum]->height;
}
else
colfunc_2s = R_DrawRepeatMaskedColumn; // render the usual 2sided single-patch packed texture
}
else
{
colfunc_2s = R_Render2sidedMultiPatchColumn; //render multipatch with no holes (no post_t info)
@ -1936,14 +1984,16 @@ void R_StoreWallRange(INT32 start, INT32 stop)
if (!backsector)
{
fixed_t texheight;
// single sided line
midtexture = texturetranslation[sidedef->midtexture];
midtexture = R_GetTextureNum(sidedef->midtexture);
texheight = textureheight[midtexture];
// a single sided line is terminal, so it must mark ends
markfloor = markceiling = true;
#ifdef ESLOPE
if (linedef->flags & ML_EFFECT2) {
if (linedef->flags & ML_DONTPEGBOTTOM)
rw_midtexturemid = frontsector->floorheight + textureheight[sidedef->midtexture] - viewz;
rw_midtexturemid = frontsector->floorheight + texheight - viewz;
else
rw_midtexturemid = frontsector->ceilingheight - viewz;
}
@ -1952,10 +2002,10 @@ void R_StoreWallRange(INT32 start, INT32 stop)
if (linedef->flags & ML_DONTPEGBOTTOM)
{
#ifdef ESLOPE
rw_midtexturemid = worldbottom + textureheight[sidedef->midtexture];
rw_midtexturemid = worldbottom + texheight;
rw_midtextureslide = floorfrontslide;
#else
vtop = frontsector->floorheight + textureheight[sidedef->midtexture];
vtop = frontsector->floorheight + texheight;
// bottom of texture at bottom
rw_midtexturemid = vtop - viewz;
#endif
@ -2187,76 +2237,50 @@ void R_StoreWallRange(INT32 start, INT32 stop)
#endif
)
{
fixed_t texheight;
// top texture
if ((linedef->flags & (ML_DONTPEGTOP) && (linedef->flags & ML_DONTPEGBOTTOM))
&& linedef->sidenum[1] != 0xffff)
{
// Special case... use offsets from 2nd side but only if it has a texture.
side_t *def = &sides[linedef->sidenum[1]];
toptexture = texturetranslation[def->toptexture];
toptexture = R_GetTextureNum(def->toptexture);
if (!toptexture) //Second side has no texture, use the first side's instead.
toptexture = texturetranslation[sidedef->toptexture];
#ifdef ESLOPE
if (!(linedef->flags & ML_EFFECT1)) { // Ignore slopes for lower/upper textures unless flag is checked
if (linedef->flags & ML_DONTPEGTOP)
rw_toptexturemid = frontsector->ceilingheight - viewz;
else
rw_toptexturemid = backsector->ceilingheight - viewz;
} else
#endif
if (linedef->flags & ML_DONTPEGTOP)
{
// top of texture at top
rw_toptexturemid = worldtop;
#ifdef ESLOPE
rw_toptextureslide = ceilingfrontslide;
#endif
}
else
{
#ifdef ESLOPE
rw_toptexturemid = worldhigh + textureheight[def->toptexture];
rw_toptextureslide = ceilingbackslide;
#else
vtop = backsector->ceilingheight + textureheight[def->toptexture];
// bottom of texture
rw_toptexturemid = vtop - viewz;
#endif
}
toptexture = R_GetTextureNum(sidedef->toptexture);
texheight = textureheight[toptexture];
}
else
{
toptexture = texturetranslation[sidedef->toptexture];
toptexture = R_GetTextureNum(sidedef->toptexture);
texheight = textureheight[toptexture];
}
#ifdef ESLOPE
if (!(linedef->flags & ML_EFFECT1)) { // Ignore slopes for lower/upper textures unless flag is checked
if (linedef->flags & ML_DONTPEGTOP)
rw_toptexturemid = frontsector->ceilingheight - viewz;
else
rw_toptexturemid = backsector->ceilingheight - viewz;
} else
#endif
if (!(linedef->flags & ML_EFFECT1)) { // Ignore slopes for lower/upper textures unless flag is checked
if (linedef->flags & ML_DONTPEGTOP)
{
// top of texture at top
rw_toptexturemid = worldtop;
#ifdef ESLOPE
rw_toptextureslide = ceilingfrontslide;
#endif
}
rw_toptexturemid = frontsector->ceilingheight - viewz;
else
{
#ifdef ESLOPE
rw_toptexturemid = worldhigh + textureheight[sidedef->toptexture];
rw_toptextureslide = ceilingbackslide;
#else
vtop = backsector->ceilingheight + textureheight[sidedef->toptexture];
// bottom of texture
rw_toptexturemid = vtop - viewz;
rw_toptexturemid = backsector->ceilingheight - viewz;
} else
#endif
if (linedef->flags & ML_DONTPEGTOP)
{
// top of texture at top
rw_toptexturemid = worldtop;
#ifdef ESLOPE
rw_toptextureslide = ceilingfrontslide;
#endif
}
else
{
#ifdef ESLOPE
rw_toptexturemid = worldhigh + texheight;
rw_toptextureslide = ceilingbackslide;
#else
vtop = backsector->ceilingheight + texheight;
// bottom of texture
rw_toptexturemid = vtop - viewz;
#endif
}
}
}
// check BOTTOM TEXTURE
@ -2267,7 +2291,7 @@ void R_StoreWallRange(INT32 start, INT32 stop)
) //seulement si VISIBLE!!!
{
// bottom texture
bottomtexture = texturetranslation[sidedef->bottomtexture];
bottomtexture = R_GetTextureNum(sidedef->bottomtexture);
#ifdef ESLOPE
if (!(linedef->flags & ML_EFFECT1)) { // Ignore slopes for lower/upper textures unless flag is checked
@ -2552,7 +2576,7 @@ void R_StoreWallRange(INT32 start, INT32 stop)
ds_p->numthicksides = numthicksides = i;
}
if (sidedef->midtexture)
if (sidedef->midtexture > 0 && sidedef->midtexture < numtextures)
{
// masked midtexture
if (!ds_p->thicksidecol)
@ -3164,12 +3188,12 @@ void R_StoreWallRange(INT32 start, INT32 stop)
if (maskedtexture && !(ds_p->silhouette & SIL_TOP))
{
ds_p->silhouette |= SIL_TOP;
ds_p->tsilheight = sidedef->midtexture ? INT32_MIN: INT32_MAX;
ds_p->tsilheight = (sidedef->midtexture > 0 && sidedef->midtexture < numtextures) ? INT32_MIN: INT32_MAX;
}
if (maskedtexture && !(ds_p->silhouette & SIL_BOTTOM))
{
ds_p->silhouette |= SIL_BOTTOM;
ds_p->bsilheight = sidedef->midtexture ? INT32_MAX: INT32_MIN;
ds_p->bsilheight = (sidedef->midtexture > 0 && sidedef->midtexture < numtextures) ? INT32_MAX: INT32_MIN;
}
ds_p++;
}

View file

@ -712,7 +712,7 @@ void R_DrawMaskedColumn(column_t *column)
dc_texturemid = basetexturemid;
}
static void R_DrawFlippedMaskedColumn(column_t *column, INT32 texheight)
void R_DrawFlippedMaskedColumn(column_t *column, INT32 texheight)
{
INT32 topscreen;
INT32 bottomscreen;
@ -1885,21 +1885,25 @@ static void R_CreateDrawNodes(void)
entry->ffloor = ds->thicksides[i];
}
}
#ifdef POLYOBJECTS_PLANES
// Check for a polyobject plane, but only if this is a front line
if (ds->curline->polyseg && ds->curline->polyseg->visplane && !ds->curline->side) {
plane = ds->curline->polyseg->visplane;
R_PlaneBounds(plane);
if (plane->low < con_clipviewtop || plane->high > vid.height || plane->high > plane->low)
;
else {
// Put it in!
entry = R_CreateDrawNode(&nodehead);
entry->plane = plane;
entry->seg = ds;
}
ds->curline->polyseg->visplane = NULL;
}
#endif
if (ds->maskedtexturecol)
{
#ifdef POLYOBJECTS_PLANES
// Check for a polyobject plane, but only if this is a front line
if (ds->curline->polyseg && ds->curline->polyseg->visplane && !ds->curline->side) {
// Put it in!
entry = R_CreateDrawNode(&nodehead);
entry->plane = ds->curline->polyseg->visplane;
entry->seg = ds;
ds->curline->polyseg->visplane->polyobj = ds->curline->polyseg;
ds->curline->polyseg->visplane = NULL;
}
#endif
entry = R_CreateDrawNode(&nodehead);
entry->seg = ds;
}
@ -1942,6 +1946,29 @@ static void R_CreateDrawNodes(void)
}
}
#ifdef POLYOBJECTS_PLANES
// find all the remaining polyobject planes and add them on the end of the list
// probably this is a terrible idea if we wanted them to be sorted properly
// but it works getting them in for now
for (i = 0; i < numPolyObjects; i++)
{
if (!PolyObjects[i].visplane)
continue;
plane = PolyObjects[i].visplane;
R_PlaneBounds(plane);
if (plane->low < con_clipviewtop || plane->high > vid.height || plane->high > plane->low)
{
PolyObjects[i].visplane = NULL;
continue;
}
entry = R_CreateDrawNode(&nodehead);
entry->plane = plane;
// note: no seg is set, for what should be obvious reasons
PolyObjects[i].visplane = NULL;
}
#endif
if (visspritecount == 0)
return;
@ -1998,13 +2025,16 @@ static void R_CreateDrawNodes(void)
if (x1 < r2->plane->minx) x1 = r2->plane->minx;
if (x2 > r2->plane->maxx) x2 = r2->plane->maxx;
for (i = x1; i <= x2; i++)
if (r2->seg) // if no seg set, assume the whole thing is in front or something stupid
{
if (r2->seg->frontscale[i] > rover->sortscale)
break;
for (i = x1; i <= x2; i++)
{
if (r2->seg->frontscale[i] > rover->sortscale)
break;
}
if (i > x2)
continue;
}
if (i > x2)
continue;
entry = R_CreateDrawNode(NULL);
(entry->prev = r2->prev)->next = entry;
@ -2880,6 +2910,7 @@ void R_AddSkins(UINT16 wadnum)
GETFLAG(STOMPDAMAGE)
GETFLAG(MARIODAMAGE)
GETFLAG(MACHINE)
GETFLAG(NOSPINDASHDUST)
#undef GETFLAG
else // let's check if it's a sound, otherwise error out

View file

@ -46,6 +46,7 @@ extern fixed_t windowtop;
extern fixed_t windowbottom;
void R_DrawMaskedColumn(column_t *column);
void R_DrawFlippedMaskedColumn(column_t *column, INT32 texheight);
void R_SortVisSprites(void);
//faB: find sprites in wadfile, replace existing, add new ones

View file

@ -607,6 +607,13 @@ void S_StartSound(const void *origin, sfxenum_t sfx_id)
sfx_id = sfx_mario6;
break;
case sfx_shield:
case sfx_wirlsg:
case sfx_forcsg:
case sfx_elemsg:
case sfx_armasg:
case sfx_s3k3e:
case sfx_s3k3f:
case sfx_s3k41:
sfx_id = sfx_mario3;
break;
case sfx_itemup:

View file

@ -49,6 +49,7 @@ void (*splatfunc)(void); // span drawer w/ transparency
void (*basespanfunc)(void); // default span func for color mode
void (*transtransfunc)(void); // translucent translated column drawer
void (*twosmultipatchfunc)(void); // for cols with transparent pixels
void (*twosmultipatchtransfunc)(void); // for cols with transparent pixels AND translucency
// ------------------
// global video state
@ -127,6 +128,7 @@ void SCR_SetMode(void)
fuzzcolfunc = R_DrawTranslucentColumn_8;
walldrawerfunc = R_DrawWallColumn_8;
twosmultipatchfunc = R_Draw2sMultiPatchColumn_8;
twosmultipatchtransfunc = R_Draw2sMultiPatchTranslucentColumn_8;
#ifdef RUSEASM
if (R_ASM)
{

View file

@ -136,6 +136,7 @@ extern void (*basespanfunc)(void);
extern void (*splatfunc)(void);
extern void (*transtransfunc)(void);
extern void (*twosmultipatchfunc)(void);
extern void (*twosmultipatchtransfunc)(void);
// -----
// CPUID

View file

@ -33,14 +33,6 @@
#pragma warning(default : 4214 4244)
#endif
#if SDL_VERSION_ATLEAST(1,3,0)
#define SDLK_EQUALS SDLK_KP_EQUALSAS400
#define SDLK_LMETA SDLK_LGUI
#define SDLK_RMETA SDLK_RGUI
#else
#define HAVE_SDLMETAKEYS
#endif
#ifdef HAVE_TTF
#include "i_ttf.h"
#endif
@ -189,14 +181,14 @@ static void SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen)
wasfullscreen = SDL_TRUE;
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
}
else if (!fullscreen && wasfullscreen)
else if (wasfullscreen)
{
wasfullscreen = SDL_FALSE;
SDL_SetWindowFullscreen(window, 0);
SDL_SetWindowSize(window, width, height);
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED_DISPLAY(1), SDL_WINDOWPOS_CENTERED_DISPLAY(1));
}
else if (!wasfullscreen)
else
{
// Reposition window only in windowed mode
SDL_SetWindowSize(window, width, height);
@ -282,129 +274,70 @@ static INT32 Impl_SDL_Scancode_To_Keycode(SDL_Scancode code)
}
switch (code)
{
case SDL_SCANCODE_F11: // F11 and F12 are
return KEY_F11; // separated from the
case SDL_SCANCODE_F12: // rest of the function
return KEY_F12; // keys
// F11 and F12 are separated from the rest of the function keys
case SDL_SCANCODE_F11: return KEY_F11;
case SDL_SCANCODE_F12: return KEY_F12;
case SDL_SCANCODE_KP_0:
return KEY_KEYPAD0;
case SDL_SCANCODE_KP_1:
return KEY_KEYPAD1;
case SDL_SCANCODE_KP_2:
return KEY_KEYPAD2;
case SDL_SCANCODE_KP_3:
return KEY_KEYPAD3;
case SDL_SCANCODE_KP_4:
return KEY_KEYPAD4;
case SDL_SCANCODE_KP_5:
return KEY_KEYPAD5;
case SDL_SCANCODE_KP_6:
return KEY_KEYPAD6;
case SDL_SCANCODE_KP_7:
return KEY_KEYPAD7;
case SDL_SCANCODE_KP_8:
return KEY_KEYPAD8;
case SDL_SCANCODE_KP_9:
return KEY_KEYPAD9;
case SDL_SCANCODE_KP_0: return KEY_KEYPAD0;
case SDL_SCANCODE_KP_1: return KEY_KEYPAD1;
case SDL_SCANCODE_KP_2: return KEY_KEYPAD2;
case SDL_SCANCODE_KP_3: return KEY_KEYPAD3;
case SDL_SCANCODE_KP_4: return KEY_KEYPAD4;
case SDL_SCANCODE_KP_5: return KEY_KEYPAD5;
case SDL_SCANCODE_KP_6: return KEY_KEYPAD6;
case SDL_SCANCODE_KP_7: return KEY_KEYPAD7;
case SDL_SCANCODE_KP_8: return KEY_KEYPAD8;
case SDL_SCANCODE_KP_9: return KEY_KEYPAD9;
case SDL_SCANCODE_RETURN:
return KEY_ENTER;
case SDL_SCANCODE_ESCAPE:
return KEY_ESCAPE;
case SDL_SCANCODE_BACKSPACE:
return KEY_BACKSPACE;
case SDL_SCANCODE_TAB:
return KEY_TAB;
case SDL_SCANCODE_SPACE:
return KEY_SPACE;
case SDL_SCANCODE_MINUS:
return KEY_MINUS;
case SDL_SCANCODE_EQUALS:
return KEY_EQUALS;
case SDL_SCANCODE_LEFTBRACKET:
return '[';
case SDL_SCANCODE_RIGHTBRACKET:
return ']';
case SDL_SCANCODE_BACKSLASH:
return '\\';
case SDL_SCANCODE_NONUSHASH:
return '#';
case SDL_SCANCODE_SEMICOLON:
return ';';
case SDL_SCANCODE_APOSTROPHE:
return '\'';
case SDL_SCANCODE_GRAVE:
return '`';
case SDL_SCANCODE_COMMA:
return ',';
case SDL_SCANCODE_PERIOD:
return '.';
case SDL_SCANCODE_SLASH:
return '/';
case SDL_SCANCODE_CAPSLOCK:
return KEY_CAPSLOCK;
case SDL_SCANCODE_PRINTSCREEN:
return 0; // undefined?
case SDL_SCANCODE_SCROLLLOCK:
return KEY_SCROLLLOCK;
case SDL_SCANCODE_PAUSE:
return KEY_PAUSE;
case SDL_SCANCODE_INSERT:
return KEY_INS;
case SDL_SCANCODE_HOME:
return KEY_HOME;
case SDL_SCANCODE_PAGEUP:
return KEY_PGUP;
case SDL_SCANCODE_DELETE:
return KEY_DEL;
case SDL_SCANCODE_END:
return KEY_END;
case SDL_SCANCODE_PAGEDOWN:
return KEY_PGDN;
case SDL_SCANCODE_RIGHT:
return KEY_RIGHTARROW;
case SDL_SCANCODE_LEFT:
return KEY_LEFTARROW;
case SDL_SCANCODE_DOWN:
return KEY_DOWNARROW;
case SDL_SCANCODE_UP:
return KEY_UPARROW;
case SDL_SCANCODE_NUMLOCKCLEAR:
return KEY_NUMLOCK;
case SDL_SCANCODE_KP_DIVIDE:
return KEY_KPADSLASH;
case SDL_SCANCODE_KP_MULTIPLY:
return '*'; // undefined?
case SDL_SCANCODE_KP_MINUS:
return KEY_MINUSPAD;
case SDL_SCANCODE_KP_PLUS:
return KEY_PLUSPAD;
case SDL_SCANCODE_KP_ENTER:
return KEY_ENTER;
case SDL_SCANCODE_KP_PERIOD:
return KEY_KPADDEL;
case SDL_SCANCODE_NONUSBACKSLASH:
return '\\';
case SDL_SCANCODE_RETURN: return KEY_ENTER;
case SDL_SCANCODE_ESCAPE: return KEY_ESCAPE;
case SDL_SCANCODE_BACKSPACE: return KEY_BACKSPACE;
case SDL_SCANCODE_TAB: return KEY_TAB;
case SDL_SCANCODE_SPACE: return KEY_SPACE;
case SDL_SCANCODE_MINUS: return KEY_MINUS;
case SDL_SCANCODE_EQUALS: return KEY_EQUALS;
case SDL_SCANCODE_LEFTBRACKET: return '[';
case SDL_SCANCODE_RIGHTBRACKET: return ']';
case SDL_SCANCODE_BACKSLASH: return '\\';
case SDL_SCANCODE_NONUSHASH: return '#';
case SDL_SCANCODE_SEMICOLON: return ';';
case SDL_SCANCODE_APOSTROPHE: return '\'';
case SDL_SCANCODE_GRAVE: return '`';
case SDL_SCANCODE_COMMA: return ',';
case SDL_SCANCODE_PERIOD: return '.';
case SDL_SCANCODE_SLASH: return '/';
case SDL_SCANCODE_CAPSLOCK: return KEY_CAPSLOCK;
case SDL_SCANCODE_PRINTSCREEN: return 0; // undefined?
case SDL_SCANCODE_SCROLLLOCK: return KEY_SCROLLLOCK;
case SDL_SCANCODE_PAUSE: return KEY_PAUSE;
case SDL_SCANCODE_INSERT: return KEY_INS;
case SDL_SCANCODE_HOME: return KEY_HOME;
case SDL_SCANCODE_PAGEUP: return KEY_PGUP;
case SDL_SCANCODE_DELETE: return KEY_DEL;
case SDL_SCANCODE_END: return KEY_END;
case SDL_SCANCODE_PAGEDOWN: return KEY_PGDN;
case SDL_SCANCODE_RIGHT: return KEY_RIGHTARROW;
case SDL_SCANCODE_LEFT: return KEY_LEFTARROW;
case SDL_SCANCODE_DOWN: return KEY_DOWNARROW;
case SDL_SCANCODE_UP: return KEY_UPARROW;
case SDL_SCANCODE_NUMLOCKCLEAR: return KEY_NUMLOCK;
case SDL_SCANCODE_KP_DIVIDE: return KEY_KPADSLASH;
case SDL_SCANCODE_KP_MULTIPLY: return '*'; // undefined?
case SDL_SCANCODE_KP_MINUS: return KEY_MINUSPAD;
case SDL_SCANCODE_KP_PLUS: return KEY_PLUSPAD;
case SDL_SCANCODE_KP_ENTER: return KEY_ENTER;
case SDL_SCANCODE_KP_PERIOD: return KEY_KPADDEL;
case SDL_SCANCODE_NONUSBACKSLASH: return '\\';
case SDL_SCANCODE_LSHIFT:
return KEY_LSHIFT;
case SDL_SCANCODE_RSHIFT:
return KEY_RSHIFT;
case SDL_SCANCODE_LCTRL:
return KEY_LCTRL;
case SDL_SCANCODE_RCTRL:
return KEY_RCTRL;
case SDL_SCANCODE_LALT:
return KEY_LALT;
case SDL_SCANCODE_RALT:
return KEY_RALT;
case SDL_SCANCODE_LGUI:
return KEY_LEFTWIN;
case SDL_SCANCODE_RGUI:
return KEY_RIGHTWIN;
default:
break;
case SDL_SCANCODE_LSHIFT: return KEY_LSHIFT;
case SDL_SCANCODE_RSHIFT: return KEY_RSHIFT;
case SDL_SCANCODE_LCTRL: return KEY_LCTRL;
case SDL_SCANCODE_RCTRL: return KEY_RCTRL;
case SDL_SCANCODE_LALT: return KEY_LALT;
case SDL_SCANCODE_RALT: return KEY_RALT;
case SDL_SCANCODE_LGUI: return KEY_LEFTWIN;
case SDL_SCANCODE_RGUI: return KEY_RIGHTWIN;
default: break;
}
#ifdef HWRENDER
DBG_Printf("Unknown incoming scancode: %d, represented %c\n",
@ -432,15 +365,10 @@ static void VID_Command_NumModes_f (void)
CONS_Printf(M_GetText("%d video mode(s) available(s)\n"), VID_NumModes());
}
// SDL2 doesn't have SDL_GetVideoSurface or a lot of the SDL_Surface flags that SDL 1.2 had
static void SurfaceInfo(const SDL_Surface *infoSurface, const char *SurfaceText)
{
#if 1
(void)infoSurface;
(void)SurfaceText;
SDL2STUB();
#else
INT32 vfBPP;
const SDL_Surface *VidSur = SDL_GetVideoSurface();
if (!infoSurface)
return;
@ -453,49 +381,12 @@ static void SurfaceInfo(const SDL_Surface *infoSurface, const char *SurfaceText)
CONS_Printf("\x82" "%s\n", SurfaceText);
CONS_Printf(M_GetText(" %ix%i at %i bit color\n"), infoSurface->w, infoSurface->h, vfBPP);
if (infoSurface->flags&SDL_HWSURFACE)
CONS_Printf("%s", M_GetText(" Stored in video memory\n"));
else if (infoSurface->flags&SDL_OPENGL)
CONS_Printf("%s", M_GetText(" Stored in an OpenGL context\n"));
else if (infoSurface->flags&SDL_PREALLOC)
if (infoSurface->flags&SDL_PREALLOC)
CONS_Printf("%s", M_GetText(" Uses preallocated memory\n"));
else
CONS_Printf("%s", M_GetText(" Stored in system memory\n"));
if (infoSurface->flags&SDL_ASYNCBLIT)
CONS_Printf("%s", M_GetText(" Uses asynchronous blits if possible\n"));
else
CONS_Printf("%s", M_GetText(" Uses synchronous blits if possible\n"));
if (infoSurface->flags&SDL_ANYFORMAT)
CONS_Printf("%s", M_GetText(" Allows any pixel-format\n"));
if (infoSurface->flags&SDL_HWPALETTE)
CONS_Printf("%s", M_GetText(" Has exclusive palette access\n"));
else if (VidSur == infoSurface)
CONS_Printf("%s", M_GetText(" Has nonexclusive palette access\n"));
if (infoSurface->flags&SDL_DOUBLEBUF)
CONS_Printf("%s", M_GetText(" Double buffered\n"));
else if (VidSur == infoSurface)
CONS_Printf("%s", M_GetText(" No hardware flipping\n"));
if (infoSurface->flags&SDL_FULLSCREEN)
CONS_Printf("%s", M_GetText(" Full screen\n"));
else if (infoSurface->flags&SDL_RESIZABLE)
CONS_Printf("%s", M_GetText(" Resizable window\n"));
else if (VidSur == infoSurface)
CONS_Printf("%s", M_GetText(" Nonresizable window\n"));
if (infoSurface->flags&SDL_HWACCEL)
CONS_Printf("%s", M_GetText(" Uses hardware acceleration blit\n"));
if (infoSurface->flags&SDL_SRCCOLORKEY)
CONS_Printf("%s", M_GetText(" Use colorkey blitting\n"));
if (infoSurface->flags&SDL_RLEACCEL)
CONS_Printf("%s", M_GetText(" Colorkey RLE acceleration blit\n"));
if (infoSurface->flags&SDL_SRCALPHA)
CONS_Printf("%s", M_GetText(" Use alpha blending acceleration blit\n"));
#endif
}
static void VID_Command_Info_f (void)
@ -579,23 +470,6 @@ static void VID_Command_Mode_f (void)
setmodeneeded = modenum+1; // request vid mode change
}
#if 0
#if defined(RPC_NO_WINDOWS_H)
static VOID MainWndproc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(hWnd);
UNREFERENCED_PARAMETER(message);
UNREFERENCED_PARAMETER(wParam);
switch (message)
{
case WM_SETTEXT:
COM_BufAddText((LPCSTR)lParam);
break;
}
}
#endif
#endif
static inline void SDLJoyRemap(event_t *event)
{
(void)event;
@ -954,218 +828,6 @@ void I_GetEvent(void)
// In order to make wheels act like buttons, we have to set their state to Up.
// This is because wheel messages don't have an up/down state.
gamekeydown[KEY_MOUSEWHEELDOWN] = gamekeydown[KEY_MOUSEWHEELUP] = 0;
#if 0
SDL_Event inputEvent;
static SDL_bool sdlquit = SDL_FALSE; //Alam: once, just once
event_t event;
if (!graphics_started)
return;
memset(&inputEvent, 0x00, sizeof(inputEvent));
while (SDL_PollEvent(&inputEvent))
{
memset(&event,0x00,sizeof (event_t));
switch (inputEvent.type)
{
case SDL_ACTIVEEVENT:
if (inputEvent.active.state & (SDL_APPACTIVE|SDL_APPINPUTFOCUS))
{
// pause music when alt-tab
if (inputEvent.active.gain /*&& !paused */)
{
static SDL_bool firsttimeonmouse = SDL_TRUE;
if (!firsttimeonmouse)
{
if (cv_usemouse.value) I_StartupMouse();
}
else firsttimeonmouse = SDL_FALSE;
//if (!netgame && !con_destlines) paused = false;
if (gamestate == GS_LEVEL)
if (!paused) I_ResumeSong(0); //resume it
}
else /*if (!paused)*/
{
if (!disable_mouse)
SDLforceUngrabMouse();
if (!netgame && gamestate == GS_LEVEL) paused = true;
memset(gamekeydown, 0, NUMKEYS);
//S_PauseSound();
if (gamestate == GS_LEVEL)
I_PauseSong(0); //pause it
}
}
if (MOUSE_MENU)
{
SDLdoUngrabMouse();
break;
}
if ((SDL_APPMOUSEFOCUS&inputEvent.active.state) && USE_MOUSEINPUT && inputEvent.active.gain)
HalfWarpMouse(realwidth, realheight);
break;
case SDL_KEYDOWN:
case SDL_KEYUP:
/// \todo inputEvent.key.which?
if (inputEvent.type == SDL_KEYUP)
event.type = ev_keyup;
else if (inputEvent.type == SDL_KEYDOWN)
event.type = ev_keydown;
else break;
event.data1 = SDLatekey(inputEvent.key.keysym.sym);
if (event.data1) D_PostEvent(&event);
break;
case SDL_MOUSEMOTION:
/// \todo inputEvent.motion.which
if (MOUSE_MENU)
{
SDLdoUngrabMouse();
break;
}
//if (USE_MOUSEINPUT) TODO SDL2 stub
{
// If the event is from warping the pointer back to middle
// of the screen then ignore it.
if ((inputEvent.motion.x == realwidth/2) &&
(inputEvent.motion.y == realheight/2))
{
break;
}
else
{
event.data2 = +inputEvent.motion.xrel;
event.data3 = -inputEvent.motion.yrel;
}
event.type = ev_mouse;
D_PostEvent(&event);
// Warp the pointer back to the middle of the window
// or we cannot move any further if it's at a border.
if ((inputEvent.motion.x < (realwidth/2 )-(realwidth/4 )) ||
(inputEvent.motion.y < (realheight/2)-(realheight/4)) ||
(inputEvent.motion.x > (realwidth/2 )+(realwidth/4 )) ||
(inputEvent.motion.y > (realheight/2)+(realheight/4) ) )
{
//if (SDL_GRAB_ON == SDL_WM_GrabInput(SDL_GRAB_QUERY) || !mousegrabok)
HalfWarpMouse(realwidth, realheight);
}
}
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
/// \todo inputEvent.button.which
if (USE_MOUSEINPUT)
{
if (inputEvent.type == SDL_MOUSEBUTTONUP)
event.type = ev_keyup;
else if (inputEvent.type == SDL_MOUSEBUTTONDOWN)
event.type = ev_keydown;
else break;
if (inputEvent.button.button==SDL_BUTTON_WHEELUP || inputEvent.button.button==SDL_BUTTON_WHEELDOWN)
{
if (inputEvent.type == SDL_MOUSEBUTTONUP)
event.data1 = 0; //Alam: dumb! this could be a real button with some mice
else
event.data1 = KEY_MOUSEWHEELUP + inputEvent.button.button - SDL_BUTTON_WHEELUP;
}
else if (inputEvent.button.button == SDL_BUTTON_MIDDLE)
event.data1 = KEY_MOUSE1+2;
else if (inputEvent.button.button == SDL_BUTTON_RIGHT)
event.data1 = KEY_MOUSE1+1;
else if (inputEvent.button.button <= MOUSEBUTTONS)
event.data1 = KEY_MOUSE1 + inputEvent.button.button - SDL_BUTTON_LEFT;
if (event.data1) D_PostEvent(&event);
}
break;
case SDL_JOYAXISMOTION:
inputEvent.jaxis.which++;
inputEvent.jaxis.axis++;
event.data1 = event.data2 = event.data3 = INT32_MAX;
if (cv_usejoystick.value == inputEvent.jaxis.which)
{
event.type = ev_joystick;
}
else if (cv_usejoystick.value == inputEvent.jaxis.which)
{
event.type = ev_joystick2;
}
else break;
//axis
if (inputEvent.jaxis.axis > JOYAXISSET*2)
break;
//vaule
if (inputEvent.jaxis.axis%2)
{
event.data1 = inputEvent.jaxis.axis / 2;
event.data2 = SDLJoyAxis(inputEvent.jaxis.value, event.type);
}
else
{
inputEvent.jaxis.axis--;
event.data1 = inputEvent.jaxis.axis / 2;
event.data3 = SDLJoyAxis(inputEvent.jaxis.value, event.type);
}
D_PostEvent(&event);
break;
case SDL_JOYBALLMOTION:
case SDL_JOYHATMOTION:
break; //NONE
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP:
inputEvent.jbutton.which++;
if (cv_usejoystick.value == inputEvent.jbutton.which)
event.data1 = KEY_JOY1;
else if (cv_usejoystick.value == inputEvent.jbutton.which)
event.data1 = KEY_2JOY1;
else break;
if (inputEvent.type == SDL_JOYBUTTONUP)
event.type = ev_keyup;
else if (inputEvent.type == SDL_JOYBUTTONDOWN)
event.type = ev_keydown;
else break;
if (inputEvent.jbutton.button < JOYBUTTONS)
event.data1 += inputEvent.jbutton.button;
else
break;
SDLJoyRemap(&event);
if (event.type != ev_console) D_PostEvent(&event);
break;
case SDL_QUIT:
if (!sdlquit)
{
sdlquit = SDL_TRUE;
M_QuitResponse('y');
}
break;
#if defined(RPC_NO_WINDOWS_H)
case SDL_SYSWMEVENT:
MainWndproc(inputEvent.syswm.msg->hwnd,
inputEvent.syswm.msg->msg,
inputEvent.syswm.msg->wParam,
inputEvent.syswm.msg->lParam);
break;
#endif
case SDL_VIDEORESIZE:
if (gamestate == GS_LEVEL || gamestate == GS_TITLESCREEN || gamestate == GS_EVALUATION)
setmodeneeded = VID_GetModeForSize(inputEvent.resize.w,inputEvent.resize.h)+1;
if (render_soft == rendermode)
{
SDLSetMode(realwidth, realheight, vid.bpp*8, surfaceFlagsW);
if (vidSurface) SDL_SetColors(vidSurface, localPalette, 0, 256);
}
else
SDLSetMode(realwidth, realheight, vid.bpp*8, surfaceFlagsW);
if (!vidSurface)
I_Error("Could not reset vidmode: %s\n",SDL_GetError());
break;
case SDL_VIDEOEXPOSE:
exposevideo = SDL_TRUE;
break;
default:
break;
}
}
//reset wheel like in win32, I don't understand it but works
#endif
}
void I_StartupMouse(void)
@ -1494,11 +1156,6 @@ void VID_PrepareModeList(void)
#endif
}
static inline void SDLESSet(void)
{
SDL2STUB();
}
INT32 VID_SetMode(INT32 modeNum)
{
SDLdoUngrabMouse();
@ -1550,6 +1207,12 @@ INT32 VID_SetMode(INT32 modeNum)
static SDL_bool Impl_CreateWindow(SDL_bool fullscreen)
{
int flags = 0;
if (rendermode == render_none) // dedicated
{
return SDL_TRUE; // Monster Iestyn -- not sure if it really matters what we return here tbh
}
if (window != NULL)
{
return SDL_FALSE;
@ -1568,38 +1231,43 @@ static SDL_bool Impl_CreateWindow(SDL_bool fullscreen)
#ifdef HWRENDER
if (rendermode == render_opengl)
{
window = SDL_CreateWindow("SRB2 "VERSIONSTRING, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
realwidth, realheight, flags | SDL_WINDOW_OPENGL);
if (window != NULL)
{
sdlglcontext = SDL_GL_CreateContext(window);
if (sdlglcontext == NULL)
{
SDL_DestroyWindow(window);
I_Error("Failed to create a GL context: %s\n", SDL_GetError());
}
else
{
SDL_GL_MakeCurrent(window, sdlglcontext);
}
}
else return SDL_FALSE;
flags |= SDL_WINDOW_OPENGL;
}
#endif
// Create a window
window = SDL_CreateWindow("SRB2 "VERSIONSTRING, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
realwidth, realheight, flags);
if (window == NULL)
{
CONS_Printf(M_GetText("Couldn't create window: %s\n"), SDL_GetError());
return SDL_FALSE;
}
// Renderer-specific stuff
#ifdef HWRENDER
if (rendermode == render_opengl)
{
sdlglcontext = SDL_GL_CreateContext(window);
if (sdlglcontext == NULL)
{
SDL_DestroyWindow(window);
I_Error("Failed to create a GL context: %s\n", SDL_GetError());
}
SDL_GL_MakeCurrent(window, sdlglcontext);
}
else
#endif
if (rendermode == render_soft)
{
window = SDL_CreateWindow("SRB2 "VERSIONSTRING, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
realwidth, realheight, flags);
if (window != NULL)
renderer = SDL_CreateRenderer(window, -1, (usesdl2soft ? SDL_RENDERER_SOFTWARE : 0) | (cv_vidwait.value && !usesdl2soft ? SDL_RENDERER_PRESENTVSYNC : 0));
if (renderer == NULL)
{
renderer = SDL_CreateRenderer(window, -1, (usesdl2soft ? SDL_RENDERER_SOFTWARE : 0) | (cv_vidwait.value && !usesdl2soft ? SDL_RENDERER_PRESENTVSYNC : 0));
if (renderer != NULL)
{
SDL_RenderSetLogicalSize(renderer, BASEVIDWIDTH, BASEVIDHEIGHT);
}
else return SDL_FALSE;
CONS_Printf(M_GetText("Couldn't create rendering context: %s\n"), SDL_GetError());
return SDL_FALSE;
}
else return SDL_FALSE;
SDL_RenderSetLogicalSize(renderer, BASEVIDWIDTH, BASEVIDHEIGHT);
}
return SDL_TRUE;
@ -1620,7 +1288,7 @@ static void Impl_SetWindowIcon(void)
{
return;
}
SDL2STUB();
//SDL2STUB(); // Monster Iestyn: why is this stubbed?
SDL_SetWindowIcon(window, icoSurface);
}
@ -1718,7 +1386,6 @@ void I_StartupGraphics(void)
borderlesswindow = M_CheckParm("-borderless");
//SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY>>1,SDL_DEFAULT_REPEAT_INTERVAL<<2);
SDLESSet();
VID_Command_ModeList_f();
#ifdef HWRENDER
if (M_CheckParm("-opengl") || rendermode == render_opengl)

View file

@ -165,8 +165,12 @@ sfxinfo_t S_sfx[NUMSFX] =
{"rail1", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR},
{"rail2", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR},
{"rlaunc", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR},
{"shield", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR},
{"shldls", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR},
{"shield", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // generic GET!
{"wirlsg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Whirlwind GET!
{"forcsg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Force GET!
{"elemsg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Elemental GET!
{"armasg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Armaggeddon GET!
{"shldls", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // You LOSE!
{"spdpad", false, 127, 0, -1, NULL, 0, -1, -1, LUMPERROR},
{"spkdth", false, 127, 0, -1, NULL, 0, -1, -1, LUMPERROR},
{"spring", false, 112, 0, -1, NULL, 0, -1, -1, LUMPERROR},
@ -183,6 +187,7 @@ sfxinfo_t S_sfx[NUMSFX] =
{"wdjump", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR},
{"mswarp", false, 60, 16, -1, NULL, 0, -1, -1, LUMPERROR},
{"mspogo", false, 60, 8, -1, NULL, 0, -1, -1, LUMPERROR},
{"boingf", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR},
// Menu, interface
{"chchng", false, 120, 0, -1, NULL, 0, -1, -1, LUMPERROR},

View file

@ -229,6 +229,10 @@ typedef enum
sfx_rail2,
sfx_rlaunc,
sfx_shield,
sfx_wirlsg,
sfx_forcsg,
sfx_elemsg,
sfx_armasg,
sfx_shldls,
sfx_spdpad,
sfx_spkdth,
@ -246,6 +250,7 @@ typedef enum
sfx_wdjump,
sfx_mswarp,
sfx_mspogo,
sfx_boingf,
// Menu, interface
sfx_chchng,

View file

@ -99,6 +99,9 @@ static patch_t *ringshield;
static patch_t *watershield;
static patch_t *bombshield;
static patch_t *pityshield;
static patch_t *flameshield;
static patch_t *bubbleshield;
static patch_t *thundershield;
static patch_t *invincibility;
static patch_t *sneakers;
static patch_t *gravboots;
@ -294,6 +297,9 @@ void ST_LoadGraphics(void)
watershield = W_CachePatchName("TVELC0", PU_HUDGFX);
bombshield = W_CachePatchName("TVARC0", PU_HUDGFX);
pityshield = W_CachePatchName("TVPIC0", PU_HUDGFX);
flameshield = W_CachePatchName("TVFLC0", PU_HUDGFX);
bubbleshield = W_CachePatchName("TVBBC0", PU_HUDGFX);
thundershield = W_CachePatchName("TVZPC0", PU_HUDGFX);
invincibility = W_CachePatchName("TVIVC0", PU_HUDGFX);
sneakers = W_CachePatchName("TVSSC0", PU_HUDGFX);
gravboots = W_CachePatchName("TVGVC0", PU_HUDGFX);
@ -576,12 +582,13 @@ static void ST_drawDebugInfo(void)
V_DrawRightAlignedString(320, height - 80, V_MONOSPACE, va("AIR: %4d, %3d", stplyr->powers[pw_underwater], stplyr->powers[pw_spacetime]));
// Flags
V_DrawRightAlignedString(304-74, height - 72, V_MONOSPACE, "PF:");
V_DrawString(304-72, height - 72, (stplyr->jumping) ? V_GREENMAP : V_REDMAP, "JM");
V_DrawString(304-54, height - 72, (stplyr->pflags & PF_JUMPED) ? V_GREENMAP : V_REDMAP, "JD");
V_DrawString(304-36, height - 72, (stplyr->pflags & PF_SPINNING) ? V_GREENMAP : V_REDMAP, "SP");
V_DrawString(304-18, height - 72, (stplyr->pflags & PF_STARTDASH) ? V_GREENMAP : V_REDMAP, "ST");
V_DrawString(304, height - 72, (stplyr->pflags & PF_THOKKED) ? V_GREENMAP : V_REDMAP, "TH");
V_DrawRightAlignedString(304-92, height - 72, V_MONOSPACE, "PF:");
V_DrawString(304-90, height - 72, (stplyr->jumping) ? V_GREENMAP : V_REDMAP, "JM");
V_DrawString(304-72, height - 72, (stplyr->pflags & PF_JUMPED) ? V_GREENMAP : V_REDMAP, "JD");
V_DrawString(304-54, height - 72, (stplyr->pflags & PF_SPINNING) ? V_GREENMAP : V_REDMAP, "SP");
V_DrawString(304-36, height - 72, (stplyr->pflags & PF_STARTDASH) ? V_GREENMAP : V_REDMAP, "ST");
V_DrawString(304-18, height - 72, (stplyr->pflags & PF_THOKKED) ? V_GREENMAP : V_REDMAP, "TH");
V_DrawString(304, height - 72, (stplyr->pflags & PF_SHIELDABILITY) ? V_GREENMAP : V_REDMAP, "SH");
V_DrawRightAlignedString(320, height - 64, V_MONOSPACE, va("CEILZ: %6d", stplyr->mo->ceilingz>>FRACBITS));
V_DrawRightAlignedString(320, height - 56, V_MONOSPACE, va("FLOORZ: %6d", stplyr->mo->floorz>>FRACBITS));
@ -806,18 +813,21 @@ static void ST_drawFirstPersonHUD(void)
return;
// Graue 06-18-2004: no V_NOSCALESTART, no SCX, no SCY, snap to right
if (player->powers[pw_shield] & SH_FORCE)
if ((player->powers[pw_shield] & SH_NOSTACK & ~SH_FORCEHP) == SH_FORCE)
{
if ((player->powers[pw_shield] & 0xFF) > 0 || leveltime & 1)
if ((player->powers[pw_shield] & SH_FORCEHP) > 0 || leveltime & 1)
p = forceshield;
}
else switch (player->powers[pw_shield] & SH_NOSTACK)
{
case SH_JUMP: p = jumpshield; break;
case SH_ELEMENTAL: p = watershield; break;
case SH_BOMB: p = bombshield; break;
case SH_ATTRACT: p = ringshield; break;
case SH_PITY: p = pityshield; break;
case SH_WHIRLWIND: p = jumpshield; break;
case SH_ELEMENTAL: p = watershield; break;
case SH_ARMAGEDDON: p = bombshield; break;
case SH_ATTRACT: p = ringshield; break;
case SH_PITY: p = pityshield; break;
case SH_FLAMEAURA: p = flameshield; break;
case SH_BUBBLEWRAP: p = bubbleshield; break;
case SH_THUNDERCOIN: p = thundershield; break;
default: break;
}

View file

@ -85,13 +85,21 @@ endif
OBJS=$(OBJDIR)/dx_error.o $(OBJDIR)/fabdxlib.o $(OBJDIR)/win_vid.o $(OBJDIR)/win_dll.o
endif
ZLIB_CFLAGS?=-I../libs/zlib
ifdef MINGW64
ZLIB_LDFLAGS?=-L../libs/zlib/win32 -lz64
else
ZLIB_LDFLAGS?=-L../libs/zlib/win32 -lz32
endif
ifndef NOPNG
ifndef PNG_CONFIG
PNG_CFLAGS?=-I../libs/libpng-src -I../libs/zlib
PNG_CFLAGS?=-I../libs/libpng-src
ifdef MINGW64
PNG_LDFLAGS?=-L../libs/libpng-src/projects -lpng64 -L../libs/zlib/win32 -lz64
PNG_LDFLAGS?=-L../libs/libpng-src/projects -lpng64
else
PNG_LDFLAGS?=-L../libs/libpng-src/projects -lpng32 -L../libs/zlib/win32 -lz32
PNG_LDFLAGS?=-L../libs/libpng-src/projects -lpng32
endif #MINGW64
endif #PNG_CONFIG
endif #NOPNG

View file

@ -20,7 +20,9 @@
#include <tchar.h>
#ifndef HAVE_SDL
#include "win_main.h"
#endif
#include "../doomdef.h" //just for VERSION
#include "win_dbg.h"
#include "../m_argv.h" //print the parameter in the log