Files
questshock/engine/src/GameSrc/status.c
T
ml a5ec8db5f7
build / build (push) Failing after 5s
Vendor the Shockolate engine and build it via a Docker image with every
dependency (SDL2, SDL2_mixer, fluidsynth-lite, a MIDI soundfont)
prebuilt, so compiling the engine needs no network access - just the
image and the engine source. Add res/assets/extract_assets.sh to pull
the game's data files out of a purchased GOG installer, and a Makefile
that assembles a runnable dist/ from the two.

Also add `make package`, which builds a redistributable tarball that
omits the proprietary game assets (shipping res/GET_ASSETS.txt instead)
plus license information for both the MIT tooling and the GPLv3 engine,
and a Gitea Actions workflow that builds and publishes it to
dl.ladkau.de on every push.
2026-07-19 15:43:38 +02:00

964 lines
29 KiB
C

/*
Copyright (C) 2015-2018 Night Dive Studios, LLC.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* $Source: r:/prj/cit/src/RCS/status.c $
* $Revision: 1.77 $
* $Author: kevin $
* $Date: 1994/11/29 11:50:25 $
*/
// Source code for handling the status indicators for the
// Citadel game screen
//
// Source is divided into two segments; first comes all the
// biorhythm stuff (upper left hand corner of the game screen)
// and second, the upper right hand healthy/suit-energy measuring
// bar graphs
#include <math.h>
#include "status.h"
#include "player.h"
#include "tools.h"
#include "gamescr.h"
#include "faketime.h"
#include "fullscrn.h"
#include "gamesys.h"
#include "physics.h"
#include "objsim.h"
#include "biotrax.h"
#include "otrip.h"
#include "curdat.h"
#include "gr2ss.h"
// Defines
#define NUM_BIO_TRACKS 8
#define GAMESCR_BIO 0
#define GAMESCR_BIO_X 5
#define GAMESCR_BIO_Y 1
//#define GAMESCR_BIO_WIDTH 149
#define GAMESCR_BIO_WIDTH 131
#define GAMESCR_BIO_HEIGHT 17
#define DIFF_BIO 1
#define DIFF_BIO_X 6
#define DIFF_BIO_Y 181
#define DIFF_BIO_WIDTH 307
#define DIFF_BIO_HEIGHT 17
#define STATUS_CHI_AMP 8
#define STATUS_BIO_X curr_bio_x
#define STATUS_BIO_Y curr_bio_y
#define STATUS_BIO_WIDTH curr_bio_w
#define STATUS_BIO_HEIGHT curr_bio_h
#define STATUS_START_OFFSET 0
#define STATUS_BIO_Y_DELTA 1
#define MAX_BIO_LENGTH 307
#define STATUS_BIO_LENGTH (STATUS_BIO_WIDTH - STATUS_START_OFFSET)
#define STATUS_BIO_TAIL 30
#define STATUS_BIO_PEAK (STATUS_BIO_HEIGHT - 3) // 3 because of zany art size
#define STATUS_BIO_X_BASE (STATUS_BIO_X + STATUS_START_OFFSET)
#define STATUS_BIO_Y_BASE (STATUS_BIO_Y + STATUS_BIO_HEIGHT - STATUS_BIO_Y_DELTA - 2)
#define SPIKE_THRESHOLD 4
#define COLOR_CHANGES 6
#define COLOR_LENGTH (STATUS_BIO_TAIL / COLOR_CHANGES)
#define MAX_TAIL_LENGTH 5
#define NO_HEIGHT 0x1f
#define INVALID_HEIGHT 0xE0
#define COLOR_BIO_MASK 0xE0 // Top Three bits signify depth of color
#define HEIGHT_BIO_MASK 0x1f // Bottom Five bits signify height
#define BIT6 0x20 // First bit of the color field.
#define COLOR_BIT_SHIFT(x) ((x) << 5)
//#define FIND_OVERLAP(x,y) (((x) - y + STATUS_BIO_LENGTH) % STATUS_BIO_LENGTH)
#define STATUS_VITALS_X 184
#define STATUS_VITALS_Y 0
#define STATUS_VITALS_WIDTH 130
#define STATUS_VITALS_HEIGHT 17
#define STATUS_VITALS_X_BASE (STATUS_VITALS_X + 4)
#define STATUS_VITALS_Y_TOP (STATUS_VITALS_Y + 1)
#define STATUS_VITALS_Y_BOTTOM (STATUS_VITALS_Y + 11)
#define STATUS_VITALS_H 8
#define STATUS_VITALS_W (STATUS_VITALS_WIDTH - 9)
#define STATUS_X 4
#define STATUS_Y 1
#define STATUS_HEIGHT 20
#define STATUS_WIDTH 312
#define GAMESCR_BIO_REF REF_IMG_bmBiorhythm
#define DIFF_BIO_REF REF_IMG_bmDiffBio
#define STATUS_RESID curr_bio_ref
#define STATUS_RES_VITALSID REF_IMG_bmVitals
#define STATUS_RES_HEALTH_ID REF_IMG_bmVitalInnardsTop
#define STATUS_RES_ENERGY_ID REF_IMG_bmVitalInnardsBottom
// Special Status Biorhythm variables
#define NO_SPIKE 0x01
#define SPIKE_NOISE 0x02
// Look ma, a new biorhythm option.
#define BOTTOMLESS 0x04 // things that fall off the bottom don't get drawn.
// For now, turn these on so it won't try to draw any biorhythm stuff
#define TIMING_PROCEDURES_OFF
//#define TIMING_CALLBACK_OFF
// Struct to contain information about tail
typedef struct {
uchar free;
int *data;
uchar height[MAX_BIO_LENGTH];
int head;
uchar tail;
uchar tail_length;
uchar color_length;
int update_time;
int counter;
int max_value;
uchar special;
uchar active;
} bio_data_block;
uchar gBioInited = FALSE;
uchar status_background[(DIFF_BIO_WIDTH + 4) * (DIFF_BIO_HEIGHT + 2)];
// uchar status_background[(266+4)*(44+2)];
uchar bio_data_buffer[NUM_BIO_TRACKS * sizeof(bio_data_block)];
#define FRAME_RATE_SCALE 20
#define LOOPLINE_SCALE 0x2F
bio_data_block *bio_data;
int bio_time_id;
int curr_bio_x, curr_bio_y;
int curr_bio_w, curr_bio_h;
short curr_bio_mode;
Ref curr_bio_ref;
static uchar track_colors[NUM_BIO_TRACKS] = {
0x66, // turquoise
0x57, // yellow green?
0x73, // blue
0x40, // orange
0x4A, // yellow
0x28, // gold
0x33, // red
0x20, // purple
};
/* bio keeps private canvas so we don't have to save/restore stuff from
the real screen canvas. */
static grs_canvas bio_canvas;
#ifdef SYNCH_BIORHYTHMS
grs_bitmap bio_bitmap;
#define BIO_BITMAP_SIZE ((GAMESCR_BIO_X + GAMESCR_BIO_WIDTH) * (GAMESCR_BIO_Y + GAMESCR_BIO_HEIGHT))
uchar bio_bitmap_bits[BIO_BITMAP_SIZE];
#endif
extern LGPoint LastCursorPos;
// Internal Prototype
uchar under_bio(int x);
void ss_save_under_set_pixel(int color, short i, short j);
void bio_set_pixel(int color, short x, short y);
void bio_restore_pixel(grs_bitmap *bmp, short x, short y);
void bio_vline(int color, int x, int y, int y1);
uchar status_track_free(int track);
uchar status_track_active(int track);
void status_track_activate(int track, uchar active);
void draw_lower_tracks(int track_number, int location);
void draw_one_location_tracks(int location);
void draw_bio_height(int track_number, int draw_location);
errtype clear_bio_tracks(void);
void clear_tail(int track_number, int delete_location);
int FIND_OVERLAP(int x, int y);
void change_bio_vars(void);
void (*bio_funcs[])(void) = {gamescr_bio_func, diff_bio_func};
uchar under_bio(int x) {
if (((x >= 16) && (x <= 22)) || ((x >= 69) && (x <= 74)) || ((x >= 114) && (x <= 208)) ||
((x >= 252) && (x <= 258)) || ((x >= 298) && (x <= 303)))
return (TRUE);
return (FALSE);
}
// ---------------------------------------------------------
// bio_set_pixel(int color, int x, int y)
//
// calls gr_set_pixel as normal, unless the biorhythms is
// going "under" something
void ss_save_under_set_pixel(int color, short i, short j) {
extern LGPoint LastCursorPos;
if (LastCursor != NULL) {
if ((LastCursorPos.x + SaveUnder.bm.w > i) && (LastCursorPos.x <= i)) {
if ((LastCursorPos.y + SaveUnder.bm.h > j) && (LastCursorPos.y <= j)) {
grs_bitmap *bm = (grs_bitmap *)(LastCursor->state);
int k = (i - LastCursorPos.x) + SaveUnder.bm.row * (j - LastCursorPos.y);
SaveUnder.bm.bits[k] = color;
k = (i - LastCursorPos.x) + bm->row * (j - LastCursorPos.y);
if (bm->bits[k]) {
return;
}
}
}
}
gr_set_pixel(color, i, j);
// mprintf("x:%i, y:%i, w:%i, h:%i\n",LastCursorPos.x,LastCursorPos.y,SaveUnder.bm.w,SaveUnder.bm.h);
}
void bio_set_pixel(int color, short x, short y) {
short x0, x1, y0, y1, i, j;
if ((curr_bio_mode == DIFF_BIO) && (under_bio(x)))
return;
if (convert_use_mode) {
x0 = SCONV_X(x);
y0 = SCONV_Y(y);
x1 = SCONV_X(x + 1);
y1 = SCONV_Y(y + 1);
for (i = x0; i < x1; i++)
for (j = y0; j < y1; j++)
ss_save_under_set_pixel(color, i, j);
} else
ss_save_under_set_pixel(color, x, y);
}
// ---------------------------------------------------------
// Restore the pixels from the offscreen background bitmap.
// ---------------------------------------------------------
void bio_restore_pixel(grs_bitmap *bmp, short x, short y) {
if ((curr_bio_mode == DIFF_BIO) && (under_bio(x)))
return;
short a, b, c, d;
STORE_CLIP(a, b, c, d);
ss_cset_cliprect(&bio_canvas, x, y, x + 1, y + 1);
ss_bitmap(bmp, STATUS_BIO_X, STATUS_BIO_Y);
RESTORE_CLIP(a, b, c, d);
}
// ---------------------------------------------------------
// bio_vline(int x, int y, int y1)
//
// calls bio_vline as normal, unless the biorhythms is
// going "under" something
void bio_vline(int color, int x, int y, int y1) {
if ((curr_bio_mode == DIFF_BIO) && (under_bio(x)))
return;
#ifdef SVGA_SUPPORT
if (convert_use_mode != 0) {
short x0, x1, i, j;
x0 = SCONV_X(x);
x1 = SCONV_X(x + 1);
y = SCONV_Y(y);
y1 = SCONV_Y(y1);
if (y > y1) {
int foo = y;
y = y1;
y1 = foo;
}
for (i = x0; i < x1; i++)
for (j = y; j <= y1; j++)
ss_save_under_set_pixel(color, i, j);
} else
#endif
{
if (y > y1) {
int foo = y;
y = y1;
y1 = foo;
}
for (; y <= y1; y++)
ss_save_under_set_pixel(color, x, y);
}
}
// ---------------------------------------------------------
// status_bio_set(short bio_mode)
//
// Tell the biorhythm code which screen we are on
short bios_x[2] = {GAMESCR_BIO_X, DIFF_BIO_X};
short bios_y[2] = {GAMESCR_BIO_Y, DIFF_BIO_Y};
short bios_w[2] = {GAMESCR_BIO_WIDTH, DIFF_BIO_WIDTH};
short bios_h[2] = {GAMESCR_BIO_HEIGHT, DIFF_BIO_HEIGHT};
Ref bio_refs[2] = {GAMESCR_BIO_REF, DIFF_BIO_REF};
grs_bitmap bio_background_bitmap;
void status_bio_set(short bio_mode) {
FrameDesc *f;
int i;
#ifdef SVGA_SUPPORT
uchar old_over = gr2ss_override;
#endif
curr_bio_mode = bio_mode;
// Assuming there are only 8 bio tracks!!!!!
/* set biorhythm positions & clipping rectangle. */
curr_bio_x = bios_x[bio_mode];
curr_bio_y = bios_y[bio_mode];
curr_bio_w = bios_w[bio_mode];
curr_bio_h = bios_h[bio_mode];
#ifdef SVGA_SUPPORT
gr2ss_override = OVERRIDE_ALL;
#endif
ss_cset_cliprect(&bio_canvas, STATUS_BIO_X, STATUS_BIO_Y, STATUS_BIO_X + STATUS_BIO_WIDTH,
STATUS_BIO_Y + STATUS_BIO_HEIGHT);
#ifdef SVGA_SUPPORT
gr2ss_override = old_over;
#endif
curr_bio_ref = bio_refs[bio_mode];
f = RefLock(STATUS_RESID);
bio_background_bitmap = f->bm;
// let's try to do the right thing!
bio_background_bitmap.bits = status_background;
LG_memcpy(bio_background_bitmap.bits, (char *)(f + 1), sizeof(char) * f->bm.w * f->bm.h);
bio_data = (bio_data_block *)bio_data_buffer;
for (i = 0; i < NUM_BIO_TRACKS; i++)
bio_data[i].free = TRUE;
RefUnlock(STATUS_RESID);
bio_funcs[curr_bio_mode]();
}
// ---------------------------------------------------------
// status_bio_init()
//
// Do the init stuff for the biorhythm. 2d should be initialized and
// screen canvas should be unchanged when this is called.
void status_bio_update_screenmode() { bio_canvas = *grd_screen_canvas; /* make copy for int routine */ }
void status_bio_init(void) {
status_bio_update_screenmode();
#ifndef TIMING_PROCEDURES_OFF
bio_time_id = tm_add_process((void (*)())status_bio_update, 0, TMD_FREQ / 140);
#endif
}
void status_bio_start(void) {
if (!full_game_3d)
gBioInited = TRUE;
#ifndef TIMING_PROCEDURES_OFF
tm_activate_process(bio_time_id);
#endif
}
void status_bio_end(void) {
gBioInited = FALSE;
// Free(bio_background_bitmap.bits);
#ifndef TIMING_PROCEDURES_OFF
tm_deactivate_process(bio_time_id);
#endif
}
// -----------------------------------------------------------
// status_bio_draw()
//
// Draw the biorhythm background
void status_bio_draw(void) {
int i;
// Draw the background map
ss_bitmap(&bio_background_bitmap, STATUS_BIO_X, STATUS_BIO_Y);
// gr_bitmap(&bio_background_bitmap, SCONV_X(STATUS_BIO_X), SCONV_Y(STATUS_BIO_Y));
// Go from left to right and draw all the tracks
for (i = 0; i < STATUS_BIO_LENGTH; i++)
draw_one_location_tracks(i);
}
// -----------------------------------------------
// Accessors for the "active" field.
uchar status_track_free(int track) { return bio_data[track].free; }
uchar status_track_active(int track) { return bio_data[track].active; }
void status_track_activate(int track, uchar active) { bio_data[track].active = active; }
//
// ----------------------------------------------------------------------
// status_bio_add()
//
// *var - address of variable to be tracked
// max_value - scale factor for value of variable
// update_time - "bio time blocks" between update (can be any value)
// track_number - track which this biorhythm should take (0 to NUM_BIO_TRACKS-1)
// tail_length - length of tail (value * STATUS_BIO_TAIL)
// special - special characteristics of this biorhythm
//
// Add a variable to be tracked by the biorhythm monitor.
// Track the NULL pointer to clear out a track slot.
//
errtype status_bio_add(int *var, int max_value, int update_time, int track_number, int tail_length, uchar special) {
bio_data_block *new_block;
uchar value;
int var_value;
if (var == NULL) // are we trying to clear out a track slot??
{
if (bio_data[track_number].free == TRUE)
return (ERR_NOEFFECT);
else {
bio_data[track_number].free = TRUE;
bio_data[track_number].active = FALSE;
return (OK);
}
}
// Make sure we have a valid entry
if ((bio_data[track_number].free == FALSE) || // unused track?
(track_number >= NUM_BIO_TRACKS) || // correct track number?
(tail_length < 1) || // non-negative tail length?
(tail_length > MAX_TAIL_LENGTH)) // too long of a tail?
return (ERR_NOEFFECT);
else {
// Initialize the new biorhythm
new_block = bio_data + track_number;
new_block->free = FALSE;
new_block->data = var;
LG_memset(&(new_block->height), INVALID_HEIGHT, sizeof(uchar) * MAX_BIO_LENGTH);
// We must first check if the variable is greater than max value, if so make it max_value
var_value = (*var > max_value) ? max_value : *var;
// Then check that it's not below 0
if (var_value < 0)
var_value = 0;
value = (var_value * STATUS_BIO_PEAK) / max_value;
// Set the end of the bio line to be the same as the initial
// This way - we don't have to check if we've started a tail
// when we erase the first value.
new_block->height[STATUS_BIO_LENGTH - 1] = value | INVALID_HEIGHT;
// Initialize the struct
new_block->head = 0; // head represents the first "new" x location
new_block->tail = FALSE;
new_block->tail_length = tail_length * STATUS_BIO_TAIL;
new_block->color_length = tail_length * COLOR_LENGTH;
new_block->update_time = update_time;
new_block->counter = 0;
new_block->max_value = max_value;
new_block->special = special;
new_block->active = TRUE;
return (OK);
}
}
errtype clear_bio_tracks() {
int i;
for (i = 0; i < NUM_BIO_TRACKS; i++)
status_bio_add(NULL, 0, 0, i, 0, 0);
return (OK);
}
//#define FIND_OVERLAP(x,y) (((x) - y + STATUS_BIO_LENGTH) % STATUS_BIO_LENGTH)
int FIND_OVERLAP(int x, int y) { return (((x)-y + STATUS_BIO_LENGTH) % STATUS_BIO_LENGTH); }
#ifdef SYNCH_BIORHYTHMS
#ifdef SVGA_SUPPORT
// ------------------------------------------------------------------------
// status_bio_synchronous()
//
// Does the synchronous blitting of biorhythm canvas
void status_bio_synchronous() {
if (!convert_use_mode)
return;
gr_push_canvas(grd_screen_canvas);
ss_scale_bitmap(&bio_bitmap, 0, 0, GAMESCR_BIO_WIDTH, GAMESCR_BIO_HEIGHT);
gr_pop_canvas();
}
#endif
#endif
// ------------------------------------------------------------------------
// status_bio_update()
//
// Draw in the biorhythm stuff
// Does clever incremental redraw (but not yet)
void status_bio_update(void) {
#ifndef TIMING_CALLBACK_OFF
uchar color;
int i;
int j;
bio_data_block *curr_blk;
int the_head;
long color_base;
int draw_location;
int var_value;
static grs_canvas *old_canvas;
if (!gBioInited)
return;
MouseLock++;
if (MouseLock > 1) {
MouseLock--;
return;
}
change_bio_vars();
old_canvas = grd_canvas;
gr_set_canvas(&bio_canvas);
curr_blk = bio_data;
#ifdef SVGA_SUPPORT
gr_push_state();
#endif
for (i = 0; i < NUM_BIO_TRACKS; i++, curr_blk++) {
if (curr_blk->free == FALSE) {
// We must check to see if this track should be drawn now,
// or must it wait until it's time
if (curr_blk->counter < curr_blk->update_time) {
curr_blk->counter++;
} else {
curr_blk->counter = 0;
color_base = track_colors[i]; // Prevent need to use index every time
the_head = curr_blk->head; // Prevent need to look inside data structure every time
// We must first check if the variable is greater than max value, if so make it max_value
var_value = (*(curr_blk->data) > curr_blk->max_value) ? curr_blk->max_value : (*(curr_blk->data));
// Then check that it's not below 0
curr_blk->height[the_head] = (var_value <= 0) ? 0 : (var_value * STATUS_BIO_PEAK) / curr_blk->max_value;
if (curr_blk->special & BOTTOMLESS && curr_blk->height[the_head] == 0)
curr_blk->height[the_head] = INVALID_HEIGHT;
// draw the head
draw_lower_tracks(i, the_head);
// Draw the first trailer pixel - We know that this pixel must exist because we drew the first pixel
// when we started the biorhythm
draw_location = FIND_OVERLAP(the_head, 1);
curr_blk->height[draw_location] |= COLOR_BIT_SHIFT(1); // shift color
draw_lower_tracks(i, draw_location);
if (curr_blk->tail == FALSE) {
// Since we don't have a tail -
// we don't know how long the biorhythm is
// Let's find out what we have to dim!!!!
for (j = 0, draw_location = the_head - curr_blk->color_length;
j < the_head / curr_blk->color_length; j++, draw_location -= curr_blk->color_length) {
color = curr_blk->height[draw_location] & COLOR_BIO_MASK;
if ((color != COLOR_BIT_SHIFT(j + 1)) || (rand() > (RAND_MAX / 2))) {
curr_blk->height[draw_location] &= HEIGHT_BIO_MASK;
curr_blk->height[draw_location] |= COLOR_BIT_SHIFT(j + 2);
draw_lower_tracks(i, draw_location);
}
}
// Have we gotten to the point where we can see the end of the tail????
if (the_head == curr_blk->tail_length) {
curr_blk->tail = TRUE; // start the tail
clear_tail(i, 0); // clear the first spot
}
} else {
// Since we have a tail - we know that we will have three spots to dim and one
// to delete from the biorhythm - let's dim the three spots first.
for (j = 0, draw_location = FIND_OVERLAP(the_head, curr_blk->color_length); j < (COLOR_CHANGES - 1);
j++, draw_location = FIND_OVERLAP(draw_location, curr_blk->color_length)) {
color = curr_blk->height[draw_location] & COLOR_BIO_MASK;
if ((color != COLOR_BIT_SHIFT(j + 1)) || (rand() > (RAND_MAX / 2))) {
curr_blk->height[draw_location] &= HEIGHT_BIO_MASK;
curr_blk->height[draw_location] |= COLOR_BIT_SHIFT(j + 2);
draw_lower_tracks(i, draw_location);
}
}
// Let's delete the end of the tail
clear_tail(i, draw_location);
}
// Advance the head
curr_blk->head = (curr_blk->head + 1) % STATUS_BIO_LENGTH;
}
}
}
#ifdef SVGA_SUPPORT
gr_pop_state();
#endif
gr_set_canvas(old_canvas);
MouseLock--;
#endif
}
// ---------------------------------------------------------------------------------------
// draw_bio_height()
//
// Note: requires bio_mouse_rect's x coordinates have already
// been set. We do this since we only draw_one_location_tracks
// calls this function.
void draw_bio_height(int track_number, int draw_location) {
bio_data_block *curr_blk;
long color;
int x;
int y;
int y1;
uchar height;
int prevHeight;
int prevSpike;
curr_blk = bio_data + track_number;
// Extract the raw "height" information
height = curr_blk->height[draw_location];
if ((height & HEIGHT_BIO_MASK) == 0 && (curr_blk->special & BOTTOMLESS))
return;
// If we have an invalid height, continue no further
if ((height & INVALID_HEIGHT) == INVALID_HEIGHT)
return;
// Extract the color offset
color = track_colors[track_number] + ((height & COLOR_BIO_MASK) >> 5);
// Extract the actual height
height &= HEIGHT_BIO_MASK;
// Get the previous height - to check for a spike
prevHeight = prevSpike = curr_blk->height[FIND_OVERLAP(draw_location, 1)];
prevHeight &= HEIGHT_BIO_MASK;
x = STATUS_BIO_X_BASE + draw_location;
y = STATUS_BIO_Y_BASE - height;
if ((abs(height - prevHeight) < SPIKE_THRESHOLD) || (curr_blk->special & NO_SPIKE) ||
((prevSpike & INVALID_HEIGHT) == INVALID_HEIGHT)) {
bio_set_pixel(color, x, y);
} else {
y1 = STATUS_BIO_Y_BASE - prevHeight;
bio_vline(color, x, y, y1);
}
}
// ----------------------------------------------------------
// clear_tail()
//
// KLC - greatly simplified this for Mac version, where the offscreen background
// bitmap is the same size as onscreen. When clearing, we simply copy the back-
// ground bitmap directly back onto the screen. bio_restore_pixel() handles
// figuring out where to restore from.
// ----------------------------------------------------------
void clear_tail(int track_number, int delete_location) {
bio_data_block *curr_blk;
int prevHeight;
int prevLocation;
int height;
int x, y, y1;
int i, delta, base;
curr_blk = bio_data + track_number;
prevLocation = FIND_OVERLAP(delete_location, 1);
prevHeight = curr_blk->height[prevLocation] & HEIGHT_BIO_MASK;
height = curr_blk->height[delete_location] & HEIGHT_BIO_MASK;
x = STATUS_BIO_X_BASE + delete_location;
y = STATUS_BIO_Y_BASE - height;
// First, check to see if we're restoring for a v-line spike.
delta = abs(height - prevHeight);
if ((delta >= SPIKE_THRESHOLD) && !((delete_location == 0) && (curr_blk->tail == FALSE))) {
y1 = STATUS_BIO_Y_BASE - prevHeight;
if (y > y1)
base = y1;
else
base = y;
for (i = 0; i < delta + 1; i++) {
bio_restore_pixel(&bio_background_bitmap, x, base + i);
}
} else {
bio_restore_pixel(&bio_background_bitmap, x, y);
}
// Invalidate the height at the delete location
curr_blk->height[delete_location] |= INVALID_HEIGHT;
draw_one_location_tracks(delete_location);
}
// ------------------------------------------------------
// draw_one_location_tracks()
//
// procedure draws all tracks at a location along
// the biorhythm
// use this to draw to preserve ordering of tracks (overlapping)
void draw_one_location_tracks(int location) {
int i;
for (i = (NUM_BIO_TRACKS - 1); i >= 0; i--) {
if (!bio_data[i].free && bio_data[i].active)
draw_bio_height(i, location);
}
}
// --------------------------------------------
// draw_lower_tracks()
//
// procedure differs from draw_one_location_tracks in that it only
// draws tracks that are under the track number, including the track
// number given. Saves a little work.
//
void draw_lower_tracks(int track_number, int location) {
int i;
for (i = track_number; i >= 0; i--) {
if (!bio_data[i].free && bio_data[i].active)
draw_bio_height(i, location);
}
}
// RANDOM SPEW STUFF
// moved here by doug to make things looking pretty
#define SIN_MAG 20
// Temporary biorhythm variables
ulong time1 = 0;
ulong time2 = 0;
ulong time3 = 0;
ulong time4 = 0;
ulong time5 = 0;
ulong time6 = 0;
ulong time7 = 0;
int bio_delta = 1;
extern int diff_sum;
extern int curr_ll;
// this simulates the heart beat!
int test_bio_var = 10;
int test_bio_var2 = 2;
// this simulates the sine wave
int test_bio_var3 = 19;
int bio_energy_var;
fix sinX = 0;
fix sinChi = 0;
void gamescr_bio_func(void) {
extern int rad_absorb, bio_absorb;
int i;
clear_bio_tracks();
// KLC - the "update_time" parameter is halved for Mac version, because we're only
// getting called 70 times/sec rather than 140.
status_bio_add(&bio_energy_var, MAX_ENERGY, 2, ENERGY_TRACK, 2, 0);
{
short ver = player_struct.hardwarez[CPTRIP(ENV_HARD_TRIPLE)];
if (ver >= 1)
status_bio_add(&bio_absorb, 24, 2, BIOHAZARD_TRACK, 3, BOTTOMLESS);
if (ver >= 2)
status_bio_add(&rad_absorb, 24, 2, RADIATION_TRACK, 3, BOTTOMLESS);
}
status_bio_add(&test_bio_var, 20, 1, HEART_TRACK, 3, 0);
status_bio_add(&test_bio_var3, SIN_MAG, 4, SINE_TRACK, 2, 0);
for (i = 0; i < NUM_BIO_TRACKS; i++)
status_track_activate(i, (player_struct.active_bio_tracks & (1 << i)) != 0);
}
void diff_bio_func(void) {
clear_bio_tracks();
// max_value, update_time, track_number, tail_length, special
status_bio_add(&diff_sum, 12, 6, 0, 2, 0);
status_bio_add(&test_bio_var, 20, 2, 1, 3, 0);
status_bio_add(&test_bio_var3, 20, 8, 2, 2, 0);
status_bio_add(&test_bio_var2, SIN_MAG, 4, 4, 3, 0);
}
uchar heart_beat = 0;
ulong heart_time = 0;
ulong heart_delay = 5;
uchar flatline_heart;
uchar chi_amp = STATUS_CHI_AMP;
extern ubyte fatigue_threshold;
#define FATIGUE_RANGE (CONTROL_MAX_VAL - SPRINT_CONTROL_THRESHOLD)
#define ENERGY_ZERO 50
void change_bio_vars(void) {
// Demo for biorhythm
int fatigue_ratio;
int fatigue_amp;
int chi_per;
if ((*tmd_ticks - heart_time) > heart_delay) {
switch (heart_beat) {
case 0:
{
int f = lg_max(0, (player_struct.fatigue - (CIT_CYCLE * fatigue_threshold)) / CIT_CYCLE);
int heart_ratio = 3400 / (f + 7);
if (heart_ratio > 500)
heart_ratio = 500;
else if (heart_ratio < 90)
heart_ratio = 90;
if (!flatline_heart && (*tmd_ticks - time1) > heart_ratio) {
test_bio_var = 19;
heart_delay = 5;
time1 = heart_time = *tmd_ticks;
heart_beat = 1;
}
} break;
case 1:
heart_beat = 2;
test_bio_var = 20;
heart_delay = 5;
break;
case 2:
heart_beat = 3;
test_bio_var = 10;
heart_delay = 7;
break;
case 3:
heart_beat = 4;
test_bio_var = 2;
heart_delay = 5;
break;
case 4:
heart_beat = 5;
test_bio_var = 1;
heart_delay = 5;
break;
case 5:
heart_beat = 0;
test_bio_var = 10;
heart_delay = 5;
break;
}
heart_time = *tmd_ticks;
}
// bio_energy_var
{
static ubyte energy_spike = 0;
static int last_val = 0;
if (bio_energy_var != last_val)
energy_spike = 3;
if (energy_spike == 0) {
bio_energy_var = ENERGY_ZERO + player_struct.energy_spend - player_struct.energy_regen;
} else {
energy_spike--;
}
last_val = bio_energy_var;
}
// MR. SINUSOID biorhythm
if (player_struct.fatigue)
fatigue_ratio = fatigue_amp = (150000 / player_struct.fatigue);
else {
fatigue_ratio = 40;
fatigue_amp = SIN_MAG;
}
if (fatigue_ratio < 5)
fatigue_ratio = 5;
else if (fatigue_ratio > 40)
fatigue_ratio = 40;
fatigue_amp -= 5;
if (fatigue_amp < 0)
fatigue_amp = 0;
else if (fatigue_amp > SIN_MAG)
fatigue_amp = SIN_MAG;
if ((*tmd_ticks - time2) > fatigue_ratio) {
sinX += FIX_UNIT / 10;
test_bio_var2 = (((FIX_UNIT - fix_fastsin(sinX)) / 2) * fatigue_amp) + 1 + ((SIN_MAG - fatigue_amp) / 2);
time2 = *tmd_ticks;
}
chi_per = chi_amp;
if (player_struct.drug_status[CPTRIP(LSD_DRUG_TRIPLE)])
chi_per <<= 1;
if (player_struct.drug_status[CPTRIP(GENIUS_DRUG_TRIPLE)])
chi_per >>= 2;
if (chi_per < 2) {
test_bio_var3 = 10;
time3 = *tmd_ticks;
sinChi = 0;
} else if ((*tmd_ticks - time3) > chi_per) {
fix s;
sinChi += FIX_UNIT / 20;
if (sinChi > 6 * FIX_UNIT)
sinChi -= 6 * FIX_UNIT;
if (sinChi < fix_make(3, 0))
s = -fix_mul(sinChi, sinChi - 3 * FIX_UNIT) * 4 / 9;
else
s = fix_mul(sinChi - 3 * FIX_UNIT, sinChi - 6 * FIX_UNIT) * 4 / 9;
test_bio_var3 = 10 + fix_int(chi_amp * s);
time3 = *tmd_ticks;
}
}