mirror of
https://github.com/Blzut3/Wolf3D-Mac.git
synced 2024-09-12 17:54:31 +00:00
1 line
13 KiB
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);
|
|
}
|
|
|