summaryrefslogtreecommitdiff
path: root/apps/plugins/lib/gray.h
blob: 52d6f6c0df53329202f4d0ae5cbf6adc32c95c14 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
/***************************************************************************
*             __________               __   ___.
*   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
*   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
*   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
*   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
*                     \/            \/     \/    \/            \/
* $Id$
*
* Grayscale framework
*
* This is a generic framework to use grayscale display within Rockbox
* plugins. It obviously does not work for the player.
*
* Copyright (C) 2004 Jens Arnold
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/

#ifndef __GRAY_H__
#define __GRAY_H__

#ifndef SIMULATOR /* not for simulator by now */
#include "plugin.h"

#ifdef HAVE_LCD_BITMAP /* and also not for the Player */

/*===========================================================================
 Public functions and definitions, to be used within plugins
 ============================================================================
 */

/*---------------------------------------------------------------------------
 Initialize the framework
 ----------------------------------------------------------------------------
 every framework needs such a function, and it has to be called as the very
 first one
 */
void gray_init(struct plugin_api* newrb);

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 General functions 
 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 */

/*---------------------------------------------------------------------------
 Prepare the grayscale display buffer
 ----------------------------------------------------------------------------
 arguments:
   gbuf      = pointer to the memory area to use (e.g. plugin buffer)
   gbuf_size = max usable size of the buffer
   width     = width in pixels  (1..112)
   bheight   = height in 8-pixel units  (1..8)
   depth     = desired number of shades - 1  (1..32)
 
 result:
   = depth  if there was enough memory
   < depth  if there wasn't enough memory. The number of displayable
            shades is smaller than desired, but it still works
   = 0      if there wasn't even enough memory for 1 bitplane (black & white)
 
 You can request any depth from 1 to 32, not just powers of 2. The routine
 performs "graceful degradation" if the memory is not sufficient for the
 desired depth. As long as there is at least enough memory for 1 bitplane,
 it creates as many bitplanes as fit into memory, although 1 bitplane will
 only deliver black & white display.

 If you need info about the memory taken by the grayscale buffer, supply an
 int* as the last parameter. This int will then contain the number of bytes
 used. The total memory needed can be calculated as follows:
   total_mem =
     sizeof(_tGraybuf)      (= 64 bytes currently)
   + sizeof(long)          (=  4 bytes)
   + (width * bheight + sizeof(long)) * depth
   + 0..3                  (longword alignment of grayscale display buffer)
 */
int gray_init_buffer(unsigned char *gbuf, int gbuf_size, int width,
                     int bheight, int depth, int *buf_taken);
                     
/*---------------------------------------------------------------------------
 Release the grayscale display buffer
 ----------------------------------------------------------------------------
 Switches the grayscale overlay off at first if it is still running,
 then sets the pointer to NULL.
 DO CALL either this function or at least gray_show_display(false)
 before you exit, otherwise nasty things may happen.
 */
void gray_release_buffer(void);

/*---------------------------------------------------------------------------
 Switch the grayscale overlay on or off
 ----------------------------------------------------------------------------
   enable = true:  the grayscale overlay is switched on if initialized
          = false: the grayscale overlay is switched off and the regular lcd
                   content is restored

 DO NOT call lcd_update() or any other api function that directly accesses
 the lcd while the grayscale overlay is running! If you need to do
 lcd_update() to update something outside the grayscale overlay area, use
 gray_deferred_update() instead.

 Other functions to avoid are:
   lcd_blit() (obviously), lcd_update_rect(), lcd_set_contrast(),
   lcd_set_invert_display(), lcd_set_flip(), lcd_roll()

 The grayscale display consumes ~50 % CPU power (for a full screen overlay,
 less if the overlay is smaller) when switched on. You can switch the overlay
 on and off as many times as you want.
 */
void gray_show_display(bool enable);

/*---------------------------------------------------------------------------
 Set position of the top left corner of the grayscale overlay
 ----------------------------------------------------------------------------
   x  = left margin in pixels
   by = top margin in 8-pixel units
 
 You may set this in a way that the overlay spills across the right or
 bottom display border. In this case it will simply be clipped by the
 LCD controller. You can even set negative values, this will clip at the
 left or top border. I did not test it, but the limits may be +127 / -128

 If you use this while the grayscale overlay is running, the now-freed area
 will be restored.
 */
void gray_position_display(int x, int by);

/*---------------------------------------------------------------------------
 Set the draw mode for subsequent drawing operations
 ----------------------------------------------------------------------------
   drawmode =
     GRAY_DRAW_INVERSE: Foreground pixels are inverted, background pixels are
                        left untouched
     GRAY_DRAW_FG:      Only foreground pixels are drawn
     GRAY_DRAW_BG:      Only background pixels are drawn
     GRAY_DRAW_SOLID:   Foreground and background pixels are drawn

 Default after initialization: GRAY_DRAW_SOLID
 */
void gray_set_drawmode(int drawmode);

/*---------------------------------------------------------------------------
 Draw modes, see above
 ----------------------------------------------------------------------------
 */
#define GRAY_DRAW_INVERSE 0
#define GRAY_DRAW_FG      1
#define GRAY_DRAW_BG      2
#define GRAY_DRAW_SOLID   3

/*---------------------------------------------------------------------------
 Set the foreground shade for subsequent drawing operations
 ----------------------------------------------------------------------------
 brightness = 0 (black) .. 255 (white)

 Default after initialization: 0
 */
void gray_set_foreground(int brightness);

/*---------------------------------------------------------------------------
 Set the background shade for subsequent drawing operations
 ----------------------------------------------------------------------------
 brightness = 0 (black) .. 255 (white)

 Default after initialization: 255
 */
void gray_set_background(int brightness);

/*---------------------------------------------------------------------------
 Set draw mode, foreground and background shades at once
 ----------------------------------------------------------------------------
 If you hand it -1 (or in fact any other out-of-bounds value) for a
 parameter, that particular setting won't be changed

 Default after initialization: GRAY_DRAW_SOLID, 0, 255
 */
void gray_set_drawinfo(int drawmode, int fg_brightness, int bg_brightness);

/*---------------------------------------------------------------------------
 Save the current display content (b&w and grayscale overlay) to an 8-bit
 BMP file in the root directory
 ----------------------------------------------------------------------------
 *
 * This one is rather slow if used with larger bit depths, but it's intended
 * primary use is for documenting the grayscale plugins. A much faster version
 * would be possible, but would take more than twice the RAM
 */
void gray_screendump(void);

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 Functions affecting the whole display
 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 */

/*---------------------------------------------------------------------------
 Clear the grayscale display (sets all pixels to white)
 ----------------------------------------------------------------------------
 */
void gray_clear_display(void);

/*---------------------------------------------------------------------------
 Set the grayscale display to all black 
 ----------------------------------------------------------------------------
 */
void gray_black_display(void);

/*---------------------------------------------------------------------------
 Do an lcd_update() to show changes done by rb->lcd_xxx() functions (in areas
 of the screen not covered by the grayscale overlay).
 ----------------------------------------------------------------------------
 If the grayscale overlay is running, the update will be done in the next
 call of the interrupt routine, otherwise it will be performed right away.
 See also comment for the gray_show_display() function.
 */
void gray_deferred_update(void);

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 Scrolling functions
 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 */

/*---------------------------------------------------------------------------
 Scroll the whole grayscale buffer left by <count> pixels
 ----------------------------------------------------------------------------
 black_border determines if the pixels scrolled in at the right are black
 or white

 Scrolling left/right by an even pixel count is almost twice as fast as
 scrolling by an odd pixel count.
 */
void gray_scroll_left(int count, bool black_border);

/*---------------------------------------------------------------------------
 Scroll the whole grayscale buffer right by <count> pixels
 ----------------------------------------------------------------------------
 black_border determines if the pixels scrolled in at the left are black
 or white

 Scrolling left/right by an even pixel count is almost twice as fast as
 scrolling by an odd pixel count.
 */
void gray_scroll_right(int count, bool black_border);

/*---------------------------------------------------------------------------
 Scroll the whole grayscale buffer up by 8 pixels
 ----------------------------------------------------------------------------
 black_border determines if the pixels scrolled in at the bottom are black
 or white

 Scrolling up/down by 8 pixels is very fast.
 */
void gray_scroll_up8(bool black_border);

/*---------------------------------------------------------------------------
 Scroll the whole grayscale buffer down by 8 pixels
 ----------------------------------------------------------------------------
 black_border determines if the pixels scrolled in at the top are black
 or white

 Scrolling up/down by 8 pixels is very fast.
 */
void gray_scroll_down8(bool black_border);

/*---------------------------------------------------------------------------
 Scroll the whole grayscale buffer up by <count> pixels (<= 7)
 ----------------------------------------------------------------------------
 black_border determines if the pixels scrolled in at the bottom are black
 or white

 Scrolling up/down pixel-wise is significantly slower than scrolling
 left/right or scrolling up/down byte-wise because it involves bit
 shifting. That's why it is asm optimized.
 */
void gray_scroll_up(int count, bool black_border);

/*---------------------------------------------------------------------------
 Scroll the whole grayscale buffer down by <count> pixels (<= 7)
 ----------------------------------------------------------------------------
 black_border determines if the pixels scrolled in at the top are black
 or white

 Scrolling up/down pixel-wise is significantly slower than scrolling
 left/right or scrolling up/down byte-wise because it involves bit
 shifting. That's why it is asm optimized.
 */
void gray_scroll_down(int count, bool black_border);

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 Pixel and line functions
 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 */

/*---------------------------------------------------------------------------
 Set a pixel with the current drawinfo
 ----------------------------------------------------------------------------
 If the drawmode is GRAY_DRAW_INVERSE, the pixel is inverted
 GRAY_DRAW_FG and GRAY_DRAW_SOLID draw the pixel in the foreground shade
 GRAY_DRAW_BG draws the pixel in the background shade
 */
void gray_drawpixel(int x, int y);

/*---------------------------------------------------------------------------
 Draw a line from (x1, y1) to (x2, y2) with the current drawinfo
 ----------------------------------------------------------------------------
 See gray_drawpixel() for details
 */
void gray_drawline(int x1, int y1, int x2, int y2);

/*---------------------------------------------------------------------------
 Draw a horizontal line from (x1, y) to (x2, y) with the current drawinfo
 ----------------------------------------------------------------------------
 See gray_drawpixel() for details
 */
void gray_horline(int x1, int x2, int y);

/*---------------------------------------------------------------------------
 Draw a vertical line from (x, y1) to (x, y2) with the current drawinfo
 ----------------------------------------------------------------------------
 See gray_drawpixel() for details
 This one uses the block drawing optimization, so it is rather fast.
 */
void gray_verline(int x, int y1, int y2);

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 Rectangle functions
 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 */

/*---------------------------------------------------------------------------
 Draw a (hollow) rectangle with the current drawinfo
 ----------------------------------------------------------------------------
 See gray_drawpixel() for details
 */
void gray_drawrect(int x, int y, int nx, int ny);

/*---------------------------------------------------------------------------
 Draw a filled rectangle with the current drawinfo
 ----------------------------------------------------------------------------
 See gray_drawpixel() for details
 This one uses the block drawing optimization, so it is rather fast.
 */
void gray_fillrect(int x, int y, int nx, int ny);

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 Bitmap functions
 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 */

/*---------------------------------------------------------------------------
 Copy a grayscale bitmap into the display
 ----------------------------------------------------------------------------
 A grayscale bitmap contains one byte for every pixel that defines the
 brightness of the pixel (0..255). Bytes are read in row-major order.
 The <stride> parameter is useful if you want to show only a part of a
 bitmap. It should always be set to the "row length" of the bitmap, so
 for displaying the whole bitmap, nx == stride.
 
 This is the only drawing function NOT using the drawinfo.
 */
void gray_drawgraymap(const unsigned char *src, int x, int y, int nx, int ny,
                      int stride);

/*---------------------------------------------------------------------------
 Display a bitmap with the current drawinfo
 ----------------------------------------------------------------------------
 The drawmode is used as described for gray_set_drawmode()

 This (now) uses the same bitmap format as the core b&w graphics routines,
 so you can use bmp2rb to generate bitmaps for use with this function as
 well.

 A bitmap contains one bit for every pixel that defines if that pixel is
 foreground (1) or background (0). Bits within a byte are arranged
 vertically, LSB at top.
 The bytes are stored in row-major order, with byte 0 being top left,
 byte 1 2nd from left etc. The first row of bytes defines pixel rows
 0..7, the second row defines pixel row 8..15 etc.

 The <stride> parameter is useful if you want to show only a part of a
 bitmap. It should always be set to the "row length" of the bitmap.
 */
void gray_drawbitmap(const unsigned char *src, int x, int y, int nx, int ny,
                     int stride);

/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 Font support
 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 */

/*---------------------------------------------------------------------------
 Set font for the font routines
 ----------------------------------------------------------------------------
 newfont can be FONT_SYSFIXED or FONT_UI the same way as with the Rockbox
 core routines
 
 Default after initialization: FONT_SYSFIXED
 */
void gray_setfont(int newfont);

/*---------------------------------------------------------------------------
 Calculate width and height of the given text in pixels when rendered with
 the currently selected font.
 ----------------------------------------------------------------------------
 This works exactly the same way as the core lcd_getstringsize(), only that
 it uses the selected font for grayscale.
 */
int gray_getstringsize(const unsigned char *str, int *w, int *h);

/*---------------------------------------------------------------------------
 Display text starting at (x, y) with the current font and drawinfo
 ----------------------------------------------------------------------------
 The drawmode is used as described for gray_set_drawmode()
 */
void gray_putsxy(int x, int y, const unsigned char *str);

/*===========================================================================
 Private functions and definitions, for use within the grayscale core only
 ============================================================================
 */

/* flag definitions */
#define _GRAY_RUNNING          0x0001  /* grayscale overlay is running */
#define _GRAY_DEFERRED_UPDATE  0x0002  /* lcd_update() requested */

/* unsigned 16 bit multiplication (a single instruction on the SH) */
#define MULU16(a, b) ((unsigned long) \
                     (((unsigned short) (a)) * ((unsigned short) (b))))

/* The grayscale buffer management structure */
typedef struct
{
    int x;
    int by;         /* 8-pixel units */
    int width;
    int height;
    int bheight;    /* 8-pixel units */
    int plane_size;
    int depth;      /* number_of_bitplanes  = (number_of_grayscales - 1) */
    int cur_plane;  /* for the timer isr */
    unsigned long randmask;    /* mask for random value in _writepixel() */
    unsigned long flags;       /* various flags, see #defines */
    unsigned long *bitpattern; /* pointer to start of pattern table */
    unsigned char *data;       /* pointer to start of bitplane data */
    unsigned long fg_pattern;  /* current foreground pattern */
    unsigned long bg_pattern;  /* current background pattern */
    int drawmode;              /* current draw mode */
    struct font *curfont;      /* current selected font */
} _tGraybuf;

/* Global variables */
extern struct plugin_api *_gray_rb;
extern _tGraybuf *_graybuf;
extern short _gray_random_buffer;

/* Global function pointers */
extern void (* const _gray_pixelfuncs[4])(int x, int y, unsigned long pattern);
extern void (* const _gray_blockfuncs[4])(unsigned char *address, unsigned mask,
                                          unsigned bits);

#endif /* HAVE_LCD_BITMAP */
#endif /* SIMULATOR */
#endif /* __GRAY_H__ */
ctions when the state is set if (st->action) st->action(mobj); seenstate[state] = 1 + st->nextstate; // killough 4/9/98 state = st->nextstate; } while (!mobj->tics && !seenstate[state]); // killough 4/9/98 if (ret && !mobj->tics) // killough 4/9/98: detect state cycles doom_printf("Warning: State Cycle Detected"); if (!--recursion) for (;(state=seenstate[i]);i=state-1) seenstate[i] = 0; // killough 4/9/98: erase memory of states return ret; } // // P_ExplodeMissile // void P_ExplodeMissile (mobj_t* mo) { mo->momx = mo->momy = mo->momz = 0; P_SetMobjState (mo, mobjinfo[mo->type].deathstate); mo->tics -= P_Random(pr_explode)&3; if (mo->tics < 1) mo->tics = 1; mo->flags &= ~MF_MISSILE; if (mo->info->deathsound) S_StartSound (mo, mo->info->deathsound); } // // P_XYMovement // // Attempts to move something if it has momentum. // void P_XYMovement (mobj_t* mo) { player_t *player; fixed_t xmove, ymove; #if 0 fixed_t ptryx; fixed_t ptryy; fixed_t xmove; fixed_t ymove; fixed_t oldx,oldy; // phares 9/10/98: reducing bobbing/momentum on ice // when up against walls #endif if (!(mo->momx | mo->momy)) // Any momentum? { if (mo->flags & MF_SKULLFLY) { // the skull slammed into something mo->flags &= ~MF_SKULLFLY; mo->momz = 0; P_SetMobjState (mo, mo->info->spawnstate); } return; } player = mo->player; if (mo->momx > MAXMOVE) mo->momx = MAXMOVE; else if (mo->momx < -MAXMOVE) mo->momx = -MAXMOVE; if (mo->momy > MAXMOVE) mo->momy = MAXMOVE; else if (mo->momy < -MAXMOVE) mo->momy = -MAXMOVE; xmove = mo->momx; ymove = mo->momy; #if 0 oldx = mo->x; // phares 9/10/98: new code to reduce bobbing/momentum oldy = mo->y; // when on ice & up against wall. These will be compared // to your x,y values later to see if you were able to move #endif do { fixed_t ptryx, ptryy; // killough 8/9/98: fix bug in original Doom source: // Large negative displacements were never considered. // This explains the tendency for Mancubus fireballs // to pass through walls. // CPhipps - compatibility optioned if (xmove > MAXMOVE/2 || ymove > MAXMOVE/2 || (!comp[comp_moveblock] && (xmove < -MAXMOVE/2 || ymove < -MAXMOVE/2))) { ptryx = mo->x + xmove/2; ptryy = mo->y + ymove/2; xmove >>= 1; ymove >>= 1; } else { ptryx = mo->x + xmove; ptryy = mo->y + ymove; xmove = ymove = 0; } // killough 3/15/98: Allow objects to drop off if (!P_TryMove (mo, ptryx, ptryy, true)) { // blocked move // killough 8/11/98: bouncing off walls // killough 10/98: // Add ability for objects other than players to bounce on ice if (!(mo->flags & MF_MISSILE) && mbf_features && (mo->flags & MF_BOUNCES || (!player && blockline && variable_friction && mo->z <= mo->floorz && P_GetFriction(mo, NULL) > ORIG_FRICTION))) { if (blockline) { fixed_t r = ((blockline->dx >> FRACBITS) * mo->momx + (blockline->dy >> FRACBITS) * mo->momy) / ((blockline->dx >> FRACBITS)*(blockline->dx >> FRACBITS)+ (blockline->dy >> FRACBITS)*(blockline->dy >> FRACBITS)); fixed_t x = FixedMul(r, blockline->dx); fixed_t y = FixedMul(r, blockline->dy); // reflect momentum away from wall mo->momx = x*2 - mo->momx; mo->momy = y*2 - mo->momy; // if under gravity, slow down in // direction perpendicular to wall. if (!(mo->flags & MF_NOGRAVITY)) { mo->momx = (mo->momx + x)/2; mo->momy = (mo->momy + y)/2; } } else mo->momx = mo->momy = 0; } else if (player) // try to slide along it P_SlideMove (mo); else if (mo->flags & MF_MISSILE) { // explode a missile if (ceilingline && ceilingline->backsector && ceilingline->backsector->ceilingpic == skyflatnum) if (demo_compatibility || // killough mo->z > ceilingline->backsector->ceilingheight) { // Hack to prevent missiles exploding // against the sky. // Does not handle sky floors. P_RemoveMobj (mo); return; } P_ExplodeMissile (mo); } else // whatever else it is, it is now standing still in (x,y) mo->momx = mo->momy = 0; } } while (xmove || ymove); // slow down #if 0 /* killough 10/98: this is unused code (except maybe in .deh files?) */ if (player && player->cheats & CF_NOMOMENTUM) { // debug option for no sliding at all mo->momx = mo->momy = 0; player->momx = player->momy = 0; /* killough 10/98 */ return; } #endif /* no friction for missiles or skulls ever, no friction when airborne */ if (mo->flags & (MF_MISSILE | MF_SKULLFLY) || mo->z > mo->floorz) return; /* killough 8/11/98: add bouncers * killough 9/15/98: add objects falling off ledges * killough 11/98: only include bouncers hanging off ledges */ if (((mo->flags & MF_BOUNCES && mo->z > mo->dropoffz) || mo->flags & MF_CORPSE || mo->intflags & MIF_FALLING) && (mo->momx > FRACUNIT/4 || mo->momx < -FRACUNIT/4 || mo->momy > FRACUNIT/4 || mo->momy < -FRACUNIT/4) && mo->floorz != mo->subsector->sector->floorheight) return; // do not stop sliding if halfway off a step with some momentum // killough 11/98: // Stop voodoo dolls that have come to rest, despite any // moving corresponding player, except in old demos: if (mo->momx > -STOPSPEED && mo->momx < STOPSPEED && mo->momy > -STOPSPEED && mo->momy < STOPSPEED && (!player || !(player->cmd.forwardmove | player->cmd.sidemove) || (player->mo != mo && compatibility_level >= lxdoom_1_compatibility))) { // if in a walking frame, stop moving // killough 10/98: // Don't affect main player when voodoo dolls stop, except in old demos: if (player && (unsigned)(player->mo->state - states - S_PLAY_RUN1) < 4 && (player->mo == mo || compatibility_level >= lxdoom_1_compatibility)) P_SetMobjState(player->mo, S_PLAY); mo->momx = mo->momy = 0; /* killough 10/98: kill any bobbing momentum too (except in voodoo dolls) * cph - DEMOSYNC - needs compatibility check? */ if (player && player->mo == mo) player->momx = player->momy = 0; } else { /* phares 3/17/98 * * Friction will have been adjusted by friction thinkers for * icy or muddy floors. Otherwise it was never touched and * remained set at ORIG_FRICTION * * killough 8/28/98: removed inefficient thinker algorithm, * instead using touching_sectorlist in P_GetFriction() to * determine friction (and thus only when it is needed). * * killough 10/98: changed to work with new bobbing method. * Reducing player momentum is no longer needed to reduce * bobbing, so ice works much better now. * * cph - DEMOSYNC - need old code for Boom demos? */ fixed_t friction = P_GetFriction(mo, NULL); mo->momx = FixedMul(mo->momx, friction); mo->momy = FixedMul(mo->momy, friction); /* killough 10/98: Always decrease player bobbing by ORIG_FRICTION. * This prevents problems with bobbing on ice, where it was not being * reduced fast enough, leading to all sorts of kludges being developed. */ if (player && player->mo == mo) /* Not voodoo dolls */ { player->momx = FixedMul(player->momx, ORIG_FRICTION); player->momy = FixedMul(player->momy, ORIG_FRICTION); } } } // // P_ZMovement // // Attempt vertical movement. static void P_ZMovement (mobj_t* mo) { /* killough 7/11/98: * BFG fireballs bounced on floors and ceilings in Pre-Beta Doom * killough 8/9/98: added support for non-missile objects bouncing * (e.g. grenade, mine, pipebomb) */ if (mo->flags & MF_BOUNCES && mo->momz) { mo->z += mo->momz; if (mo->z <= mo->floorz) { /* bounce off floors */ mo->z = mo->floorz; if (mo->momz < 0) { mo->momz = -mo->momz; if (!(mo->flags & MF_NOGRAVITY)) { /* bounce back with decay */ mo->momz = mo->flags & MF_FLOAT ? // floaters fall slowly mo->flags & MF_DROPOFF ? // DROPOFF indicates rate FixedMul(mo->momz, (fixed_t)(FRACUNIT*.85)) : FixedMul(mo->momz, (fixed_t)(FRACUNIT*.70)) : FixedMul(mo->momz, (fixed_t)(FRACUNIT*.45)) ; /* Bring it to rest below a certain speed */ if (abs(mo->momz) <= mo->info->mass*(GRAVITY*4/256)) mo->momz = 0; } /* killough 11/98: touchy objects explode on impact */ if (mo->flags & MF_TOUCHY && mo->intflags & MIF_ARMED && mo->health > 0) P_DamageMobj(mo, NULL, NULL, mo->health); else if (mo->flags & MF_FLOAT && sentient(mo)) goto floater; return; } } else if (mo->z >= mo->ceilingz - mo->height) { /* bounce off ceilings */ mo->z = mo->ceilingz - mo->height; if (mo->momz > 0) { if (mo->subsector->sector->ceilingpic != skyflatnum) mo->momz = -mo->momz; /* always bounce off non-sky ceiling */ else if (mo->flags & MF_MISSILE) P_RemoveMobj(mo); /* missiles don't bounce off skies */ else if (mo->flags & MF_NOGRAVITY) mo->momz = -mo->momz; // bounce unless under gravity if (mo->flags & MF_FLOAT && sentient(mo)) goto floater; return; } } else { if (!(mo->flags & MF_NOGRAVITY)) /* free-fall under gravity */ mo->momz -= mo->info->mass*(GRAVITY/256); if (mo->flags & MF_FLOAT && sentient(mo)) goto floater; return; } /* came to a stop */ mo->momz = 0; if (mo->flags & MF_MISSILE) { if (ceilingline && ceilingline->backsector && ceilingline->backsector->ceilingpic == skyflatnum && mo->z > ceilingline->backsector->ceilingheight) P_RemoveMobj(mo); /* don't explode on skies */ else P_ExplodeMissile(mo); } if (mo->flags & MF_FLOAT && sentient(mo)) goto floater; return; } /* killough 8/9/98: end bouncing object code */ // check for smooth step up if (mo->player && mo->player->mo == mo && // killough 5/12/98: exclude voodoo dolls mo->z < mo->floorz) { mo->player->viewheight -= mo->floorz-mo->z; mo->player->deltaviewheight = (VIEWHEIGHT - mo->player->viewheight)>>3; } // adjust altitude mo->z += mo->momz; floater: if ((mo->flags & MF_FLOAT) && mo->target) // float down towards target if too close if (!((mo->flags ^ MF_FLOAT) & (MF_FLOAT | MF_SKULLFLY | MF_INFLOAT)) && mo->target) /* killough 11/98: simplify */ { fixed_t delta; if (P_AproxDistance(mo->x - mo->target->x, mo->y - mo->target->y) < abs(delta = mo->target->z + (mo->height>>1) - mo->z)*3) mo->z += delta < 0 ? -FLOATSPEED : FLOATSPEED; } // clip movement if (mo->z <= mo->floorz) { // hit the floor /* Note (id): * somebody left this after the setting momz to 0, * kinda useless there. * cph - This was the a bug in the linuxdoom-1.10 source which * caused it not to sync Doom 2 v1.9 demos. Someone * added the above comment and moved up the following code. So * demos would desync in close lost soul fights. * Note that this only applies to original Doom 1 or Doom2 demos - * Final Doom and Ultimate Doom. So we test demo_compatibility *and* * gamemission. (Note we assume that Doom1 is always Ult Doom, which * seems to hold for most published demos.) */ int correct_lost_soul_bounce = !demo_compatibility || (gamemission != doom2); if (correct_lost_soul_bounce && mo->flags & MF_SKULLFLY) mo->momz = -mo->momz; // the skull slammed into something if (mo->momz < 0) { /* killough 11/98: touchy objects explode on impact */ if (mo->flags & MF_TOUCHY && mo->intflags & MIF_ARMED && mo->health > 0) P_DamageMobj(mo, NULL, NULL, mo->health); else if (mo->player && /* killough 5/12/98: exclude voodoo dolls */ mo->player->mo == mo && mo->momz < -GRAVITY*8) { // Squat down. // Decrease viewheight for a moment // after hitting the ground (hard), // and utter appropriate sound. mo->player->deltaviewheight = mo->momz>>3; if (mo->health) /* cph - prevent "oof" when dead */ S_StartSound (mo, sfx_oof); } mo->momz = 0; } mo->z = mo->floorz; /* cph 2001/05/26 - * See lost soul bouncing comment above. We need this here for bug * compatibility with original Doom2 v1.9 - if a soul is charging and * hit by a raising floor this incorrectly reverses its Y momentum. */ if (!correct_lost_soul_bounce && mo->flags & MF_SKULLFLY) mo->momz = -mo->momz; // the skull slammed into something if ( (mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP) ) { P_ExplodeMissile (mo); return; } } else // still above the floor // phares if (!(mo->flags & MF_NOGRAVITY)) { if (!mo->momz) mo->momz = -GRAVITY; mo->momz -= GRAVITY; } if (mo->z + mo->height > mo->ceilingz) { // hit the ceiling if (mo->momz > 0) mo->momz = 0; mo->z = mo->ceilingz - mo->height; if (mo->flags & MF_SKULLFLY) mo->momz = -mo->momz; // the skull slammed into something if ( (mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP) ) { P_ExplodeMissile (mo); return; } } } // // P_NightmareRespawn // void P_NightmareRespawn(mobj_t* mobj) { fixed_t x; fixed_t y; fixed_t z; subsector_t* ss; mobj_t* mo; mapthing_t* mthing; x = mobj->spawnpoint.x << FRACBITS; y = mobj->spawnpoint.y << FRACBITS; /* haleyjd: stupid nightmare respawning bug fix * * 08/09/00: compatibility added, time to ramble :) * This fixes the notorious nightmare respawning bug that causes monsters * that didn't spawn at level startup to respawn at the point (0,0) * regardless of that point's nature. SMMU and Eternity need this for * script-spawned things like Halif Swordsmythe, as well. * * cph - copied from eternity, except comp_respawnfix becomes comp_respawn * and the logic is reversed (i.e. like the rest of comp_ it *disables* * the fix) */ if(!comp[comp_respawn] && !x && !y) { // spawnpoint was zeroed out, so use point of death instead x = mobj->x; y = mobj->y; } // something is occupying its position? if (!P_CheckPosition (mobj, x, y) ) return; // no respwan // spawn a teleport fog at old spot // because of removal of the body? mo = P_SpawnMobj (mobj->x, mobj->y, mobj->subsector->sector->floorheight, MT_TFOG); // initiate teleport sound S_StartSound (mo, sfx_telept); // spawn a teleport fog at the new spot ss = R_PointInSubsector (x,y); mo = P_SpawnMobj (x, y, ss->sector->floorheight , MT_TFOG); S_StartSound (mo, sfx_telept); // spawn the new monster mthing = &mobj->spawnpoint; if (mobj->info->flags & MF_SPAWNCEILING) z = ONCEILINGZ; else z = ONFLOORZ; // inherit attributes from deceased one mo = P_SpawnMobj (x,y,z, mobj->type); mo->spawnpoint = mobj->spawnpoint; mo->angle = ANG45 * (mthing->angle/45); if (mthing->options & MTF_AMBUSH) mo->flags |= MF_AMBUSH; /* killough 11/98: transfer friendliness from deceased */ mo->flags = (mo->flags & ~MF_FRIEND) | (mobj->flags & MF_FRIEND); mo->reactiontime = 18; // remove the old monster, P_RemoveMobj (mobj); } // // P_MobjThinker // void P_MobjThinker (mobj_t* mobj) { // killough 11/98: // removed old code which looked at target references // (we use pointer reference counting now) // momentum movement if (mobj->momx | mobj->momy || mobj->flags & MF_SKULLFLY) { P_XYMovement(mobj); if (mobj->thinker.function != P_MobjThinker) // cph - Must've been removed return; // killough - mobj was removed } if (mobj->z != mobj->floorz || mobj->momz) { P_ZMovement(mobj); if (mobj->thinker.function != P_MobjThinker) // cph - Must've been removed return; // killough - mobj was removed } else if (!(mobj->momx | mobj->momy) && !sentient(mobj)) { // non-sentient objects at rest mobj->intflags |= MIF_ARMED; // arm a mine which has come to rest // killough 9/12/98: objects fall off ledges if they are hanging off // slightly push off of ledge if hanging more than halfway off if (mobj->z > mobj->dropoffz && // Only objects contacting dropoff !(mobj->flags & MF_NOGRAVITY) && // Only objects which fall !comp[comp_falloff]) // Not in old demos P_ApplyTorque(mobj); // Apply torque else mobj->intflags &= ~MIF_FALLING, mobj->gear = 0; // Reset torque } // cycle through states, // calling action functions at transitions if (mobj->tics != -1) { mobj->tics--; // you can cycle through multiple states in a tic if (!mobj->tics) if (!P_SetMobjState (mobj, mobj->state->nextstate) ) return; // freed itself } else { // check for nightmare respawn if (! (mobj->flags & MF_COUNTKILL) ) return; if (!respawnmonsters) return; mobj->movecount++; if (mobj->movecount < 12*35) return; if (leveltime & 31) return; if (P_Random (pr_respawn) > 4) return; P_NightmareRespawn (mobj); } } // // P_SpawnMobj // mobj_t* P_SpawnMobj(fixed_t x,fixed_t y,fixed_t z,mobjtype_t type) { mobj_t* mobj; state_t* st; mobjinfo_t* info; mobj = Z_Malloc (sizeof(*mobj), PU_LEVEL, NULL); memset (mobj, 0, sizeof (*mobj)); info = &mobjinfo[type]; mobj->type = type; mobj->info = info; mobj->x = x; mobj->y = y; mobj->radius = info->radius; mobj->height = info->height; // phares mobj->flags = info->flags; /* killough 8/23/98: no friends, bouncers, or touchy things in old demos */ if (!mbf_features) mobj->flags &= ~(MF_BOUNCES | MF_FRIEND | MF_TOUCHY); else if (type == MT_PLAYER) // Except in old demos, players mobj->flags |= MF_FRIEND; // are always friends. mobj->health = info->spawnhealth; if (gameskill != sk_nightmare) mobj->reactiontime = info->reactiontime; mobj->lastlook = P_Random (pr_lastlook) % MAXPLAYERS; // do not set the state with P_SetMobjState, // because action routines can not be called yet st = &states[info->spawnstate]; mobj->state = st; mobj->tics = st->tics; mobj->sprite = st->sprite; mobj->frame = st->frame; mobj->touching_sectorlist = NULL; // NULL head of sector list // phares 3/13/98 // set subsector and/or block links P_SetThingPosition (mobj); mobj->dropoffz = /* killough 11/98: for tracking dropoffs */ mobj->floorz = mobj->subsector->sector->floorheight; mobj->ceilingz = mobj->subsector->sector->ceilingheight; mobj->z = z == ONFLOORZ ? mobj->floorz : z == ONCEILINGZ ? mobj->ceilingz - mobj->height : z; mobj->thinker.function = P_MobjThinker; mobj->above_thing = 0; // phares mobj->below_thing = 0; // phares mobj->target = mobj->tracer = mobj->lastenemy = NULL; P_AddThinker (&mobj->thinker); if (!((mobj->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL))) totallive++; return mobj; } mapthing_t itemrespawnque[ITEMQUESIZE]; int itemrespawntime[ITEMQUESIZE]; int iquehead; int iquetail; // // P_RemoveMobj // void P_RemoveMobj (mobj_t* mobj) { if ((mobj->flags & MF_SPECIAL) && !(mobj->flags & MF_DROPPED) && (mobj->type != MT_INV) && (mobj->type != MT_INS)) { itemrespawnque[iquehead] = mobj->spawnpoint; itemrespawntime[iquehead] = leveltime; iquehead = (iquehead+1)&(ITEMQUESIZE-1); // lose one off the end? if (iquehead == iquetail) iquetail = (iquetail+1)&(ITEMQUESIZE-1); } // unlink from sector and block lists P_UnsetThingPosition (mobj); // Delete all nodes on the current sector_list phares 3/16/98 if (sector_list) { P_DelSeclist(sector_list); sector_list = NULL; } // stop any playing sound S_StopSound (mobj); // killough 11/98: // // Remove any references to other mobjs. // // Older demos might depend on the fields being left alone, however, // if multiple thinkers reference each other indirectly before the // end of the current tic. // CPhipps - only leave dead references in old demos; I hope lxdoom_1 level // demos are rare and don't rely on this. I hope. if ((compatibility_level >= lxdoom_1_compatibility) || (!demorecording && !demoplayback)) { P_SetTarget(&mobj->target, NULL); P_SetTarget(&mobj->tracer, NULL); P_SetTarget(&mobj->lastenemy, NULL); } // free block // P_RemoveThinker ((thinker_t*)mobj); P_RemoveThinker (&mobj->thinker); } /* * P_FindDoomedNum * * Finds a mobj type with a matching doomednum * * killough 8/24/98: rewrote to use hashing */ int P_FindDoomedNum(unsigned type) { static struct { int first, next; } *hash; register int i; if (!hash) { hash = Z_Malloc(sizeof (*hash) * NUMMOBJTYPES, PU_CACHE, (void*)(void*) &hash); for (i=0; i<NUMMOBJTYPES; i++) hash[i].first = NUMMOBJTYPES; for (i=0; i<NUMMOBJTYPES; i++) if (mobjinfo[i].doomednum != -1) { unsigned h = (unsigned) mobjinfo[i].doomednum % NUMMOBJTYPES; hash[i].next = hash[h].first; hash[h].first = i; } } i = hash[type % NUMMOBJTYPES].first; while ((i < NUMMOBJTYPES) && ((unsigned)mobjinfo[i].doomednum != type)) i = hash[i].next; return i; } // // P_RespawnSpecials // void P_RespawnSpecials (void) { fixed_t x; fixed_t y; fixed_t z; subsector_t* ss; mobj_t* mo; mapthing_t* mthing; int i; // only respawn items in deathmatch if (deathmatch != 2) return; // nothing left to respawn? if (iquehead == iquetail) return; // wait at least 30 seconds if (leveltime - itemrespawntime[iquetail] < 30*35) return; mthing = &itemrespawnque[iquetail]; x = mthing->x << FRACBITS; y = mthing->y << FRACBITS; // spawn a teleport fog at the new spot ss = R_PointInSubsector (x,y); mo = P_SpawnMobj (x, y, ss->sector->floorheight , MT_IFOG); S_StartSound (mo, sfx_itmbk); // find which type to spawn /* killough 8/23/98: use table for faster lookup */ i = P_FindDoomedNum(mthing->type); // spawn it if (mobjinfo[i].flags & MF_SPAWNCEILING) z = ONCEILINGZ; else z = ONFLOORZ; mo = P_SpawnMobj (x,y,z, i); mo->spawnpoint = *mthing; mo->angle = ANG45 * (mthing->angle/45); // pull it from the queue iquetail = (iquetail+1)&(ITEMQUESIZE-1); } // // P_SpawnPlayer // Called when a player is spawned on the level. // Most of the player structure stays unchanged // between levels. // extern byte playernumtotrans[MAXPLAYERS]; void P_SpawnPlayer (mapthing_t* mthing) { player_t* p; fixed_t x; fixed_t y; fixed_t z; mobj_t* mobj; int i; // not playing? if (!playeringame[mthing->type-1]) return; p = &players[mthing->type-1]; if (p->playerstate == PST_REBORN) G_PlayerReborn (mthing->type-1); x = mthing->x << FRACBITS; y = mthing->y << FRACBITS; z = ONFLOORZ; mobj = P_SpawnMobj (x,y,z, MT_PLAYER); // set color translations for player sprites if (mthing->type > 0) mobj->flags |= playernumtotrans[mthing->type-1]<<MF_TRANSSHIFT; mobj->angle = ANG45 * (mthing->angle/45); mobj->player = p; mobj->health = p->health; p->mo = mobj; p->playerstate = PST_LIVE; p->refire = 0; p->message = NULL; p->damagecount = 0; p->bonuscount = 0; p->extralight = 0; p->fixedcolormap = 0; p->viewheight = VIEWHEIGHT; p->momx = p->momy = 0; // killough 10/98: initialize bobbing to 0. // setup gun psprite P_SetupPsprites (p); // give all cards in death match mode if (deathmatch) for (i = 0 ; i < NUMCARDS ; i++) p->cards[i] = true; if (mthing->type-1 == consoleplayer) { ST_Start(); // wake up the status bar HU_Start(); // wake up the heads up text } } // // P_SpawnMapThing // The fields of the mapthing should // already be in host byte order. // void P_SpawnMapThing (mapthing_t* mthing) { int i; //int bit; mobj_t* mobj; fixed_t x; fixed_t y; fixed_t z; // killough 2/26/98: Ignore type-0 things as NOPs // phares 5/14/98: Ignore Player 5-8 starts (for now) switch(mthing->type) { case 0: case DEN_PLAYER5: case DEN_PLAYER6: case DEN_PLAYER7: case DEN_PLAYER8: return; } // killough 11/98: clear flags unused by Doom // // We clear the flags unused in Doom if we see flag mask 256 set, since // it is reserved to be 0 under the new scheme. A 1 in this reserved bit // indicates it's a Doom wad made by a Doom editor which puts 1's in // bits that weren't used in Doom (such as HellMaker wads). So we should // then simply ignore all upper bits. if (demo_compatibility || (compatibility_level >= lxdoom_1_compatibility && mthing->options & MTF_RESERVED)) { if (!demo_compatibility) // cph - Add warning about bad thing flags printf("P_SpawnMapThing: correcting bad flags (%u) (thing type %d)\n", mthing->options, mthing->type); mthing->options &= MTF_EASY|MTF_NORMAL|MTF_HARD|MTF_AMBUSH|MTF_NOTSINGLE; } // count deathmatch start positions // doom2.exe has at most 10 deathmatch starts if (mthing->type == 11 && (!compatibility || deathmatch_p-deathmatchstarts < 10)) { // 1/11/98 killough -- new code removes limit on deathmatch starts: size_t offset = deathmatch_p - deathmatchstarts; if (offset >= num_deathmatchstarts) { num_deathmatchstarts = num_deathmatchstarts ? num_deathmatchstarts*2 : 16; deathmatchstarts = realloc(deathmatchstarts, num_deathmatchstarts * sizeof(*deathmatchstarts)); deathmatch_p = deathmatchstarts + offset; } memcpy(deathmatch_p++, mthing, sizeof(*mthing)); return; } // check for players specially if (mthing->type <= 4 && mthing->type > 0) // killough 2/26/98 -- fix crashes { #ifdef DOGS // killough 7/19/98: Marine's best friend :) if (!netgame && mthing->type > 1 && mthing->type <= dogs+1 && !players[mthing->type-1].secretcount) { // use secretcount to avoid multiple dogs in case of multiple starts players[mthing->type-1].secretcount = 1; // killough 10/98: force it to be a friend mthing->options |= MTF_FRIEND; i = MT_DOGS; goto spawnit; } #endif // save spots for respawning in network games playerstarts[mthing->type-1] = *mthing; if (!deathmatch) P_SpawnPlayer (mthing); return; } // check for apropriate skill level /* jff "not single" thing flag */ if (!netgame && mthing->options & MTF_NOTSINGLE) return; //jff 3/30/98 implement "not deathmatch" thing flag if (netgame && deathmatch && mthing->options & MTF_NOTDM) return; //jff 3/30/98 implement "not cooperative" thing flag if (netgame && !deathmatch && mthing->options & MTF_NOTCOOP) return; // killough 11/98: simplify if (gameskill == sk_baby || gameskill == sk_easy ? !(mthing->options & MTF_EASY) : gameskill == sk_hard || gameskill == sk_nightmare ? !(mthing->options & MTF_HARD) : !(mthing->options & MTF_NORMAL)) return; // find which type to spawn // killough 8/23/98: use table for faster lookup i = P_FindDoomedNum(mthing->type); // phares 5/16/98: // Do not abort because of an unknown thing. Ignore it, but post a // warning message for the player. if (i == NUMMOBJTYPES) { doom_printf("Unknown Thing type %i at (%i, %i)",mthing->type,mthing->x,mthing->y); return; } // don't spawn keycards and players in deathmatch if (deathmatch && mobjinfo[i].flags & MF_NOTDMATCH) return; // don't spawn any monsters if -nomonsters if (nomonsters && (i == MT_SKULL || (mobjinfo[i].flags & MF_COUNTKILL))) return; // spawn it #ifdef DOGS spawnit: #endif x = mthing->x << FRACBITS; y = mthing->y << FRACBITS; if (mobjinfo[i].flags & MF_SPAWNCEILING) z = ONCEILINGZ; else z = ONFLOORZ; mobj = P_SpawnMobj (x,y,z, i); mobj->spawnpoint = *mthing; if (mobj->tics > 0) mobj->tics = 1 + (P_Random (pr_spawnthing) % mobj->tics); if (!(mobj->flags & MF_FRIEND) && mthing->options & MTF_FRIEND && mbf_features) { mobj->flags |= MF_FRIEND; // killough 10/98: P_UpdateThinker(&mobj->thinker); // transfer friendliness flag } /* killough 7/20/98: exclude friends */ if (!((mobj->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL))) totalkills++; if (mobj->flags & MF_COUNTITEM) totalitems++; mobj->angle = ANG45 * (mthing->angle/45); if (mthing->options & MTF_AMBUSH) mobj->flags |= MF_AMBUSH; } // // GAME SPAWN FUNCTIONS // // // P_SpawnPuff // extern fixed_t attackrange; void P_SpawnPuff(fixed_t x,fixed_t y,fixed_t z) { mobj_t* th; // killough 5/5/98: remove dependence on order of evaluation: int t = P_Random(pr_spawnpuff); z += (t - P_Random(pr_spawnpuff))<<10; th = P_SpawnMobj (x,y,z, MT_PUFF); th->momz = FRACUNIT; th->tics -= P_Random(pr_spawnpuff)&3; if (th->tics < 1) th->tics = 1; // don't make punches spark on the wall if (attackrange == MELEERANGE) P_SetMobjState (th, S_PUFF3); } // // P_SpawnBlood // void P_SpawnBlood(fixed_t x,fixed_t y,fixed_t z,int damage) { mobj_t* th; // killough 5/5/98: remove dependence on order of evaluation: int t = P_Random(pr_spawnblood); z += (t - P_Random(pr_spawnblood))<<10; th = P_SpawnMobj(x,y,z, MT_BLOOD); th->momz = FRACUNIT*2; th->tics -= P_Random(pr_spawnblood)&3; if (th->tics < 1) th->tics = 1; if (damage <= 12 && damage >= 9) P_SetMobjState (th,S_BLOOD2); else if (damage < 9) P_SetMobjState (th,S_BLOOD3); } // // P_CheckMissileSpawn // Moves the missile forward a bit // and possibly explodes it right there. // void P_CheckMissileSpawn (mobj_t* th) { th->tics -= P_Random(pr_missile)&3; if (th->tics < 1) th->tics = 1; // move a little forward so an angle can // be computed if it immediately explodes th->x += (th->momx>>1); th->y += (th->momy>>1); th->z += (th->momz>>1); // killough 8/12/98: for non-missile objects (e.g. grenades) if (!(th->flags & MF_MISSILE) && mbf_features) return; // killough 3/15/98: no dropoff (really = don't care for missiles) if (!P_TryMove (th, th->x, th->y, false)) P_ExplodeMissile (th); } // // P_SpawnMissile // mobj_t* P_SpawnMissile(mobj_t* source,mobj_t* dest,mobjtype_t type) { mobj_t* th; angle_t an; int dist; th = P_SpawnMobj (source->x,source->y,source->z + 4*8*FRACUNIT,type); if (th->info->seesound) S_StartSound (th, th->info->seesound); P_SetTarget(&th->target, source); // where it came from an = R_PointToAngle2 (source->x, source->y, dest->x, dest->y); // fuzzy player if (dest->flags & MF_SHADOW) { // killough 5/5/98: remove dependence on order of evaluation: int t = P_Random(pr_shadow); an += (t - P_Random(pr_shadow))<<20; } th->angle = an; an >>= ANGLETOFINESHIFT; th->momx = FixedMul (th->info->speed, finecosine[an]); th->momy = FixedMul (th->info->speed, finesine[an]); dist = P_AproxDistance (dest->x - source->x, dest->y - source->y); dist = dist / th->info->speed; if (dist < 1) dist = 1; th->momz = (dest->z - source->z) / dist; P_CheckMissileSpawn (th); return th; } // // P_SpawnPlayerMissile // Tries to aim at a nearby monster // void P_SpawnPlayerMissile(mobj_t* source,mobjtype_t type) { mobj_t *th; fixed_t x, y, z, slope = 0; // see which target is to be aimed at angle_t an = source->angle; // killough 7/19/98: autoaiming was not in original beta { // killough 8/2/98: prefer autoaiming at enemies uint_64_t mask = mbf_features ? MF_FRIEND : 0; do { slope = P_AimLineAttack(source, an, 16*64*FRACUNIT, mask); if (!linetarget) slope = P_AimLineAttack(source, an += 1<<26, 16*64*FRACUNIT, mask); if (!linetarget) slope = P_AimLineAttack(source, an -= 2<<26, 16*64*FRACUNIT, mask); if (!linetarget) an = source->angle, slope = 0; } while (mask && (mask=0, !linetarget)); // killough 8/2/98 } x = source->x; y = source->y; z = source->z + 4*8*FRACUNIT; th = P_SpawnMobj (x,y,z, type); if (th->info->seesound) S_StartSound (th, th->info->seesound); P_SetTarget(&th->target, source); th->angle = an; th->momx = FixedMul(th->info->speed,finecosine[an>>ANGLETOFINESHIFT]); th->momy = FixedMul(th->info->speed,finesine[an>>ANGLETOFINESHIFT]); th->momz = FixedMul(th->info->speed,slope); P_CheckMissileSpawn(th); }