Wolf3D-Mac/EnThink.c

1 line
13 KiB
C

#include "wolfdef.h"
/**********************************
Drops a bonus item at the x,y of the actor
If there are no free item spots, nothing is done.
**********************************/
void PlaceItemType(Word shape,actor_t *ActorPtr)
{
Word tile;
Word x,y;
static_t *StaticPtr;
if (numstatics>=MAXSTATICS) { /* Already full? */
return; /* Get out! */
}
StaticPtr = &statics[numstatics]; /* Get pointer to the record */
/* drop bonus items on closest tile, rather than goal tile (unless it is a closing door) */
x = ActorPtr->x >> FRACBITS;
y = ActorPtr->y >> FRACBITS;
tile = tilemap[y][x];
if ( (tile&TI_BLOCKMOVE) && !(tile &TI_ACTOR) ) {
x = ActorPtr->goalx;
y = ActorPtr->goaly;
}
StaticPtr->pic = shape;
StaticPtr->x = (x<<FRACBITS)|0x80;
StaticPtr->y = (y<<FRACBITS)|0x80;
StaticPtr->areanumber = ActorPtr->areanumber;
tilemap[y][x] |= TI_GETABLE; /* Mark as getable */
++numstatics; /* A new static */
}
/**********************************
Kill an actor
Also drop off any items you can get from a dead guy.
**********************************/
void KillActor(actor_t *ActorPtr)
{
Word x,y;
GivePoints(classinfo[ActorPtr->class].points); /* Award the score */
switch(ActorPtr->class) { /* Drop anything special? */
case CL_SS:
PlaceItemType(S_MACHINEGUN,ActorPtr); /* Give a gun */
break;
case CL_OFFICER:
case CL_MUTANT:
case CL_GUARD:
PlaceItemType(S_AMMO,ActorPtr); /* Drop some ammo */
break;
case CL_HANS:
case CL_SCHABBS:
case CL_TRANS:
case CL_UBER:
case CL_DKNIGHT:
PlaceItemType(S_G_KEY,ActorPtr); /* Drop a key */
break;
}
++gamestate.killcount; /* I killed someone! */
ActorPtr->flags = FL_DEAD; /* remove old actor marker*/
tilemap[ActorPtr->goaly][ActorPtr->goalx] &= ~TI_ACTOR;
x = ActorPtr->x >> FRACBITS;
y = ActorPtr->y >> FRACBITS;
tilemap[y][x] |= TI_BODY; /* body flag on most apparant, no matter what */
NewState(ActorPtr,classinfo[ActorPtr->class].deathstate); /* start the death animation */
}
/**********************************
Does damage points to enemy actor, either putting it into a stun frame or
killing it.
Called when an enemy is hit.
**********************************/
static Word PainTick;
void DamageActor(Word damage,actor_t *ActorPtr)
{
stateindex_t pain;
madenoise = TRUE; /* You made some noise! */
/* do double damage if shooting a non attack mode actor*/
if ( !(ActorPtr->flags & FL_ACTIVE) ) {
if (difficulty<3) { /* Death incarnate? */
damage <<= 1;
}
FirstSighting(ActorPtr); /* Put into combat mode*/
}
if (damage >= ActorPtr->hitpoints) { /* Did I kill it? */
KillActor(ActorPtr); /* Die!! */
return;
}
ActorPtr->hitpoints -= damage; /* Remove the damage */
if (ActorPtr->class == CL_MECHAHITLER && ActorPtr->hitpoints <= 250 && ActorPtr->hitpoints+damage > 250) {
/* hitler losing armor */
PlaySound(SND_SHIT); /* Remove armor */
pain = ST_MHITLER_DIE1;
} else {
if ((ReadTick() - PainTick) >= 30) {
PainTick = ReadTick();
PlaySound(SND_PAIN); /* Ow!! */
}
pain = classinfo[ActorPtr->class].painstate; /* Do pain */
}
if (pain) { /* some classes don't have pain frames */
if (ActorPtr->state != pain) { /* Already in pain? */
NewState(ActorPtr,pain);
}
}
}
/**********************************
Throw a Missile at the player
**********************************/
void A_Throw(actor_t *ActorPtr)
{
Word angle;
int speed;
missile_t *MissilePtr;
PlaySound(SND_ROCKET|0x8000);
MissilePtr = GetNewMissile(); /* Create a missile */
MissilePtr->x = ActorPtr->x;
MissilePtr->y = ActorPtr->y;
MissilePtr->areanumber = ActorPtr->areanumber;
/* get direction from enemy to player */
angle = PointToAngle(ActorPtr->x,ActorPtr->y);
angle >>= SHORTTOANGLESHIFT;
speed = costable[angle];
speed = speed/5;
MissilePtr->xspeed = -speed;
speed = sintable[angle];
speed = speed/5;
MissilePtr->yspeed = speed;
MissilePtr->pic = S_NEEDLE; /* Hurl a needle */
MissilePtr->flags = MF_HITPLAYER | MF_HITSTATICS; /* Can hit the player */
MissilePtr->type = MI_NEEDLE; /* Needle missile */
}
/**********************************
Launch a rocket at the player
**********************************/
void A_Launch(actor_t *ActorPtr)
{
Word angle;
int speed;
missile_t *MissilePtr;
PlaySound(SND_ROCKET|0x8000);
MissilePtr = GetNewMissile();
MissilePtr->x = ActorPtr->x;
MissilePtr->y = ActorPtr->y;
MissilePtr->areanumber = ActorPtr->areanumber;
/* get direction from player to boss*/
angle = PointToAngle (ActorPtr->x,ActorPtr->y);
angle >>= SHORTTOANGLESHIFT;
speed = costable[angle];
speed = speed/5;
MissilePtr->xspeed = -speed;
speed = sintable[angle];
speed = speed/5;
MissilePtr->yspeed = speed;
MissilePtr->pic = S_ENMISSILE; /* Rocket */
MissilePtr->flags = MF_HITPLAYER | MF_HITSTATICS;
MissilePtr->type = MI_EMISSILE;
A_Shoot(ActorPtr); /* also shoot a bullet */
}
/**********************************
Scream a death sound
**********************************/
void A_Scream(actor_t *ActorPtr)
{
Word Sound,i;
Sound = classinfo[ActorPtr->class].deathsound; /* Get the sound # */
if (Sound==SND_EDIE) { /* Normal death sound? */
if (w_rnd()&1) { /* Play one randomly */
++Sound;
}
i = 0;
do {
StopSound(NaziSound[i]); /* Kill all Nazi voices */
} while (++i<4);
}
PlaySound(Sound); /* Play the sound */
}
/**********************************
Body hitting the ground
**********************************/
void A_Thud(actor_t *ActorPtr)
{
PlaySound(SND_BODYFALL);
}
/**********************************
You win the game!
**********************************/
void A_Victory(actor_t *ActorPtr)
{
playstate = EX_COMPLETED;
}
/**********************************
Drop Hitler's armor and let hitler run around
**********************************/
void A_HitlerMorph(actor_t *ActorPtr)
{
missile_t *MissilePtr;
/* Use an inert missile for the armor remnants */
MissilePtr = GetNewMissile();
MissilePtr->x = ActorPtr->x; /* Pass the armor x,y */
MissilePtr->y = ActorPtr->y;
MissilePtr->areanumber = ActorPtr->areanumber;
MissilePtr->xspeed = 0; /* No motion */
MissilePtr->yspeed = 0;
MissilePtr->flags = 0;
MissilePtr->type = -1; /* Maximum time */
MissilePtr->pic = S_MHITLER_DIE4; /* Set the picture */
ActorPtr->class = CL_HITLER; /* Convert to true hitler */
ActorPtr->speed = 40/4; /* faster without armor*/
}
/**********************************
Try to damage the player, based on skill level and player's speed
**********************************/
void A_Shoot(actor_t *ActorPtr)
{
Word damage; /* Damage to inflict */
Word distance;
if (!areabyplayer[MapPtr->areasoundnum[ActorPtr->areanumber]]) { /* In the same area? */
return;
}
madenoise = TRUE; /* I made a sound! */
if (ActorPtr->class >= CL_HANS) { /* Boss? */
PlaySound(SND_BIGGUN|0x8000); /* Boom! */
} else {
PlaySound(SND_GUNSHT|0x8000); /* Bang! */
}
if (!CheckLine(ActorPtr)) { /* Player is behind a wall*/
return; /* Can't shoot! */
}
distance = CalcDistance(ActorPtr); /* How far? (0-4095 range) */
if (distance >= TILEGLOBAL*16) { /* Too far away? */
return;
}
if (ActorPtr->class == CL_OFFICER || ActorPtr->class >= CL_HANS) { /* better shots */
if (distance < (16*16)) {
distance = 0; /* Zap the distance */
} else {
distance -= (16*16);
}
}
if (playermoving) { /* harder to hit when moving*/
if (distance >= (224*16)) {
return;
}
distance += (32*16);
}
/* see if the shot was a hit*/
if ((w_rnd()*16)>distance) {
switch(difficulty) {
case 0:
damage = (w_rnd()&3)+1;
break;
case 1:
damage = (w_rnd()&7)+1;
break;
default:
damage = (w_rnd()&7)+3;
}
if (distance<(32*16)) {
damage <<= 2;
} else if (distance<(64*16)) {
damage <<= 1;
}
TakeDamage(damage,ActorPtr->x,ActorPtr->y); /* Hit the player (Pass the killer's x,y) */
}
}
/**********************************
Bite the player
**********************************/
void A_Bite(actor_t *ActorPtr)
{
Word dmg;
PlaySound(SND_DOGBARK); /* Take a bite! */
if (CalcDistance(ActorPtr)<=BITERANGE) { /* In range? */
switch (difficulty) {
case 0:
dmg = (w_rnd()&3)+3; /* Small bite */
break;
case 1:
dmg = (w_rnd()&7)+3; /* Medium bite */
break;
default:
dmg = (w_rnd()&15)+4; /* BIG bite */
}
TakeDamage(dmg,ActorPtr->x,ActorPtr->y); /* Pass along the damage */
}
}
/**********************************
Return the distance between the player and this actor
**********************************/
Word CalcDistance(actor_t *ActorPtr)
{
Word absdx;
Word absdy;
absdx = w_abs(ActorPtr->x - actors[0].x);
absdy = w_abs(ActorPtr->y - actors[0].y);
return (absdx > absdy) ? absdx : absdy; /* Return the larger */
}
/**********************************
Called every few frames to check for sighting and attacking the player
**********************************/
Word shootchance[8] = {256,64,32,24,20,16,12,8};
void A_Target(actor_t *ActorPtr)
{
Word chance; /* % chance of hit */
Word distance; /* Distance of critters */
if (!areabyplayer[MapPtr->areasoundnum[ActorPtr->areanumber]] || !CheckLine(ActorPtr)) {
ActorPtr->flags &= ~FL_SEEPLAYER; /* Can't see you */
return;
}
ActorPtr->flags |= FL_SEEPLAYER; /* I see you */
distance = CalcDistance(ActorPtr); /* Get the distance */
if (distance < BITERANGE) { /* always attack when this close */
goto attack;
}
if (ActorPtr->class == CL_DOG) { /* Dogs can only bite */
return;
}
if (ActorPtr->class == CL_SCHABBS && distance <= TILEGLOBAL*4) {
goto attack; /* Dr. schabbs always attacks */
}
if (distance >= TILEGLOBAL*8) { /* Too far? */
return;
}
chance = shootchance[distance>>FRACBITS]; /* Get the base chance */
if (difficulty >= 2) {
chance <<= 1; /* Increase chance */
}
if (w_rnd() < chance) {
attack: /* go into attack frame*/
NewState(ActorPtr,classinfo[ActorPtr->class].attackstate);
}
}
/**********************************
MechaHitler takes a step
**********************************/
void A_MechStep(actor_t *ActorPtr)
{
PlaySound(SND_MECHSTEP|0x8000); /* Step sound */
A_Target(ActorPtr); /* Shoot player */
}
/**********************************
Chase the player
**********************************/
void T_Chase(actor_t *ActorPtr)
{
Word move;
/* if still centered in a tile, try to find a move */
if (ActorPtr->flags & FL_NOTMOVING) {
if (ActorPtr->flags & FL_WAITDOOR) {
TryWalk(ActorPtr); /* Waiting for a door to open*/
} else if (ActorPtr->flags & FL_SEEPLAYER) {
SelectDodgeDir(ActorPtr); /* Dodge the player's bullets */
} else {
SelectChaseDir(ActorPtr); /* Directly chase the player */
}
if (ActorPtr->flags & FL_NOTMOVING) {
return; /* Still blocked in */
}
}
/* OPTIMIZE: integral steps / tile movement */
/* cover some distance*/
move = ActorPtr->speed*TicCount; /* this could be put in the class info array*/
while (move) {
if (move < ActorPtr->distance) {
MoveActor(ActorPtr,move); /* Move one step */
return;
}
/* reached goal tile, so select another one*/
move -= ActorPtr->distance;
MoveActor(ActorPtr,ActorPtr->distance); /* move the last 1 to center*/
if (ActorPtr->flags & FL_SEEPLAYER) {
SelectDodgeDir(ActorPtr); /* Dodge the player */
} else {
SelectChaseDir(ActorPtr); /* Directly chase the player */
}
if (ActorPtr->flags & FL_NOTMOVING) {
return; /* object is blocked in*/
}
}
}
/**********************************
Move all actors for a single frame
Actions are performed as the state is entered
**********************************/
typedef void (*call_t)(actor_t *ActorPtr);
static void A_Nothing(actor_t *ActorPtr) {}
call_t thinkcalls[] = {
A_Nothing, /* No action */
T_Stand, /* Stand at attention */
T_Chase /* Chase the player */
};
call_t actioncalls[] = {
A_Nothing,
A_Target,
A_Shoot,
A_Bite,
A_Throw,
A_Launch,
A_HitlerMorph,
A_MechStep,
A_Victory,
A_Scream,
A_Thud
};
void MoveActors(void)
{
Word i; /* Index */
state_t *StatePtr; /* Pointer to state logic */
actor_t *ActorPtr; /* Pointer to Actor code */
if (numactors<2) { /* No actors to check? */
return;
}
i = 1; /* Init index */
ActorPtr = &actors[1]; /* Init the pointer to the actors */
do {
if (!(ActorPtr->flags&FL_ACTIVE) /* Is this actor in view? */
&& !areabyplayer[MapPtr->areasoundnum[ActorPtr->areanumber]]) {
goto Skip;
}
StatePtr = &states[ActorPtr->state]; /* Get the current state */
if (ActorPtr->ticcount>TicCount) { /* Count down the time */
ActorPtr->ticcount-=TicCount;
} else { /* change state if time's up */
ActorPtr->state = StatePtr->next; /* Set the next state */
StatePtr = &states[ActorPtr->state]; /* Get the new state ptr */
ActorPtr->ticcount = StatePtr->tictime; /* Reset the time */
ActorPtr->pic = StatePtr->shapenum; /* Set the new picture # */
/* action think */
actioncalls[StatePtr->action](ActorPtr); /* Call the code */
}
thinkcalls[StatePtr->think](ActorPtr); /* Perform the action */
Skip: /* Next entry */
++ActorPtr;
} while (++i<numactors);
}