ref: ee35eec9bac04ce55e5fe1ae1eeb5428b47d91a8
dir: /src/rt_actor.c/
/* Copyright (C) 1994-1995 Apogee Software, Ltd. Copyright (C) 2002-2015 icculus.org, GNU/Linux port Copyright (C) 2017-2018 Steven LeVesque 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 2 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. You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. */ #include <string.h> #include <stdlib.h> #include "rt_def.h" #include "rt_sound.h" #include "rt_door.h" #include "rt_ted.h" #include "rt_draw.h" #include "watcom.h" #include "z_zone.h" #include "w_wad.h" #include "lumpy.h" #include "gmove.h" #include "states.h" #include "rt_sqrt.h" #include "rt_stat.h" #include "sprites.h" #include "rt_actor.h" #include "rt_game.h" #include "rt_main.h" #include "rt_playr.h" #include "rt_util.h" #include "rt_rand.h" #include "rt_menu.h" #include "_rt_acto.h" #include "rt_cfg.h" #include "rt_floor.h" #include "engine.h" #include "develop.h" #include "rt_view.h" #include "isr.h" #include "rt_com.h" #include "rt_scale.h" #include "modexlib.h" #include "rt_net.h" #include "rt_msg.h" #include "fx_man.h" #include "queue.h" #define SGN(x) (((x) > 0)?(1):(-1)) #define WILEYBLITZCHANCE 20 #define GIBSOUND SD_GIBSPLASHSND #define ACTORTHUDSND SD_BODYLANDSND #define ACTORLANDSND SD_PLAYERLANDSND //========================== Global Variables =================================================== #define SHP(difficulty,ob) (starthitpoints[difficulty][ob->obclass]) #define CAP_OSCUROS_HITPOINTS(ob) \ { \ if (ob->hitpoints > (SHP(gamestate.difficulty,ob)<<1)) \ ob->hitpoints = (SHP(gamestate.difficulty,ob)<<1); \ } boolean ludicrousgibs=false; short colheight[15]; byte deathshapeoffset[8] = {0,7,7,8,8,9,8,7}; unsigned long MAXFUNCTION,MINFUNCTION,MAXSTATE,MINSTATE; objtype *PLAYER0MISSILE; objtype *SCREENEYE; objtype *FIRSTACTOR,*LASTACTOR; objtype *FIRSTFREE,*LASTFREE; objtype *lastactive,*firstactive,**objlist; objtype *firstareaactor[NUMAREAS+1],*lastareaactor[NUMAREAS+1]; int objcount; byte RANDOMACTORTYPE[10]; #if (SHAREWARE == 0) _2Dpoint SNAKEPATH[512]; #endif misc_stuff mstruct,*MISCVARS = &mstruct; int angletodir[ANGLES]; objtype *new; void *actorat[MAPSIZE][MAPSIZE]; exit_t playstate; void T_SlideDownScreen(objtype*); basic_actor_sounds BAS[NUMCLASSES+3] = { {0,0,0,0,0}, {0,0,0,0,0}, {0,SD_LOWGUARD1SEESND,SD_LOWGUARDFIRESND,SD_LOWGUARDOUCHSND,SD_LOWGUARD1DIESND}, {0,SD_HIGHGUARD1SEESND,SD_HIGHGUARDFIRESND,SD_HIGHGUARDOUCHSND,SD_HIGHGUARDDIESND}, {0,SD_OVERP1SEESND,SD_OVERPFIRESND,SD_OVERPOUCHSND,SD_OVERPDIESND}, {0,SD_STRIKE1SEESND,SD_STRIKEFIRESND,SD_STRIKEOUCHSND,SD_STRIKEDIESND}, {0,SD_BLITZ1SEESND,SD_BLITZFIRESND,SD_BLITZOUCHSND,SD_BLITZDIESND}, {0,SD_ENFORCERSEESND,SD_ENFORCERFIRESND,SD_ENFORCEROUCHSND,SD_ENFORCERDIESND}, {0,SD_MONKSEESND,SD_MONKGRABSND,SD_MONKOUCHSND,SD_MONKDIESND}, {0,SD_FIREMONKSEESND,SD_FIREMONKFIRESND,SD_FIREMONKOUCHSND,SD_FIREMONKDIESND}, {0,SD_ROBOTSEESND,SD_ROBOTFIRESND,0,SD_ROBOTDIESND}, //bosses {SD_DARIANSAY1,SD_DARIANSEESND,SD_DARIANFIRESND,0,SD_DARIANDIESND}, {SD_KRISTSAY1,SD_KRISTSEESND,SD_KRISTFIRESND,0,SD_KRISTDIESND}, {0,SD_NMESEESND,SD_NMEFIRE1SND,0,SD_NMEDIESND}, {SD_DARKMONKSAY1,SD_DARKMONKSEESND,SD_DARKMONKFIRE1SND,0,SD_DARKMONKDIESND}, {SD_SNAKESAY1,SD_SNAKESEESND,SD_SNAKESPITSND,0,SD_SNAKEDIESND}, //specials {0,SD_EMPLACEMENTSEESND,SD_EMPLACEMENTFIRESND,0,0}, {0,SD_ROBOTSEESND,SD_ROBOTFIRESND,0,SD_ROBOTDIESND}, //wallop {0,0,0,0,0}, //pillar {SD_FIREJETSND,0,0,0,0}, //firejet {SD_BLADESPINSND,0,0,0,0}, //blade {SD_CYLINDERMOVESND,0,0,0,0}, //crushcol {SD_BOULDERROLLSND,0,0,SD_BOULDERHITSND,0}, //boulder {SD_SPEARSTABSND,0,0,0,0}, //spear {0,0,0,0,0}, //gasgrate {SD_SPRINGBOARDSND,0,0,0,0}, //spring {0,0,0,0,0}, //shuriken {SD_FIREBALLSND,0,0,SD_FIREBALLHITSND,0}, //wallfire {0,0,0,0,0}, //net {SD_KRISTMINEBEEPSND,0,0,0,0}, //h_mine {0,0,0,0,0}, //grenade {0,0,0,0,0}, //fireball {0,0,0,0,0}, //dmfball {0,0,0,0,0}, //bigshuriken {0,0,0,0,0}, //missile {0,0,0,0,0}, //NMEsaucer {0,0,0,0,0}, //dm_weapon {0,0,0,0,0}, //dm_heatseek {0,0,0,0,0}, //dm_spit {SD_MISSILEFLYSND,0,SD_BAZOOKAFIRESND,SD_MISSILEHITSND,0}, {SD_MISSILEFLYSND,0,SD_FIREBOMBFIRESND,SD_MISSILEHITSND,0}, {SD_MISSILEFLYSND,0,SD_HEATSEEKFIRESND,SD_MISSILEHITSND,0}, {SD_MISSILEFLYSND,0,SD_DRUNKFIRESND,SD_MISSILEHITSND,0}, {SD_FLAMEWALLSND,0,SD_FLAMEWALLFIRESND,SD_FIREHITSND,0}, {SD_MISSILEFLYSND,0,SD_SPLITFIRESND,SD_MISSILEHITSND,0}, {SD_GRAVSND,0,SD_GRAVFIRESND,SD_GRAVHITSND,0}, {SD_GRAVSND,0,SD_GODMODEFIRESND,SD_GRAVHITSND,0} }; //========================== Local Variables ================================================== extern boolean dopefish; boolean Masterdisk; static objtype *SNAKEHEAD,*SNAKEEND,*PARTICLE_GENERATOR,*EXPLOSIONS; #if (SHAREWARE == 0) static int OLDTILEX,OLDTILEY; #endif static char *debugstr[] = { "inerttype", "player", "lowguard", "highguard", "overpatrol", "strikeguard", "blitzguard", "triadenforcer", "deathmonk", "dfiremonk", "roboguard", "b_darian", "b_heinrich", "b_darkmonk", "b_roboboss", "b_darksnake", "patrolgun", "wallop", "pillar", "firejet", "blade", "crushcol", "boulder", "spear", "gasgrate", "spring", "shuriken", "wallfire", "net", "h_mine", "grenade", "fireball", "dmfball", "bigshuriken", "missile", "NMEsaucer", "dm_weapon", "dm_heatseek", "dm_spit", "p_bazooka", "p_firebomb", "p_heatseek", "p_drunkmissile", "p_firewall", "p_splitmissile", "p_kes", "p_godball", "collectorobj" }; static int starthitpoints[4][NUMENEMIES+2] = { {0,0,30,35,50,40,45,425,200,200,100,1500,2500,3000,3000,-1,200,2}, {0,0,40,50,55,50,50,475,250,250,125,2300,3400,4500,3600,-1,250,2}, {0,0,50,65,60,60,60,525,275,300,150,2400,3600,5000,4500,-1,300,2}, {0,0,60,80,70,70,75,525,300,350,175,2800,3800,5900,4800,-1,350,2} }; static statobj_t *touchsprite = NULL; static const byte dirdiff[8][8] = {{0,1,2,3,4,3,2,1},{1,0,1,2,3,4,3,2}, {2,1,0,1,2,3,4,3},{3,2,1,0,1,2,3,4}, {4,3,2,1,0,1,2,3},{3,4,3,2,1,0,1,2}, {2,3,4,3,2,1,0,1},{1,2,3,4,3,2,1,0} }; static const byte dirorder[8][2] = {{southeast,northeast},{east,north}, {northeast,northwest},{north,west}, {northwest,southwest},{west,south}, {southwest,southeast},{south,east} }; #if (SHAREWARE == 0) static const byte dirdiff16[16][16] = { {0,1,2,3,4,5,6,7,8,7,6,5,4,3,2,1}, {1,0,1,2,3,4,5,6,7,8,7,6,5,4,3,2}, {2,1,0,1,2,3,4,5,6,7,8,7,6,5,4,3}, {3,2,1,0,1,2,3,4,5,6,7,8,7,6,5,4}, {4,3,2,1,0,1,2,3,4,5,6,7,8,7,6,5}, {5,4,3,2,1,0,1,2,3,4,5,6,7,8,7,6}, {6,5,4,3,2,1,0,1,2,3,4,5,6,7,8,7}, {7,6,5,4,3,2,1,0,1,2,3,4,5,6,7,8}, {8,7,6,5,4,3,2,1,0,1,2,3,4,5,6,7}, {7,8,7,6,5,4,3,2,1,0,1,2,3,4,5,6}, {6,7,8,7,6,5,4,3,2,1,0,1,2,3,4,5}, {5,6,7,8,7,6,5,4,3,2,1,0,1,2,3,4}, {4,5,6,7,8,7,6,5,4,3,2,1,0,1,2,3}, {3,4,5,6,7,8,7,6,5,4,3,2,1,0,1,2}, {2,3,4,5,6,7,8,7,6,5,4,3,2,1,0,1}, {1,2,3,4,5,6,7,8,7,6,5,4,3,2,1,0} }; #endif static const byte dirorder16[16][2] = { {15,1}, {0,2}, {1,3}, {2,4}, {3,5}, {4,6}, {5,7}, {6,8}, {7,9}, {8,10}, {9,11}, {10,12}, {11,13}, {12,14}, {13,15}, {14,0} }; //static byte opposite16[16] = {8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7}; #if (SHAREWARE == 0) static statetype * UPDATE_STATES[NUMSTATES][NUMENEMIES] = { { &s_lowgrdstand,&s_highgrdstand,&s_opstand,&s_strikestand, &s_blitzstand,&s_enforcerstand,&s_dmonkstand,&s_firemonkstand, &s_robogrdstand,&s_darianstand,&s_heinrichstand,NULL, &s_darkmonkstand,NULL,&s_gunstand,&s_wallstand }, { &s_lowgrdpath1,&s_highgrdpath1,&s_oppath1,&s_strikepath1, &s_blitzpath1,&s_enforcerpath1,&s_dmonkpath1,&s_firemonkpath1, &s_robogrdpath1,NULL,NULL,NULL, NULL,NULL,NULL,&s_wallpath }, { &s_lowgrdcollide,&s_highgrdcollide,&s_opcollide,&s_strikecollide, &s_blitzcollide,&s_enforcercollide,&s_dmonkcollide,&s_firemonkcollide, &s_robogrdcollide,&s_dariancollide,NULL,NULL, NULL,NULL,NULL,&s_wallcollide }, { &s_lowgrdcollide2,&s_highgrdcollide2,&s_opcollide2,&s_strikecollide2, &s_blitzcollide2,&s_enforcercollide2,&s_dmonkcollide2,&s_firemonkcollide2, &s_robogrdcollide2,&s_dariancollide2,NULL,NULL, NULL,NULL,NULL,&s_wallcollide }, { &s_lowgrdchase1,&s_highgrdchase1,&s_opchase1,&s_strikechase1, &s_blitzchase1,&s_enforcerchase1,&s_dmonkchase1,&s_firemonkchase1, NULL/*se1*/,&s_darianchase1,&s_heinrichchase,&s_NMEchase, &s_darkmonkchase1,NULL,&s_gunstand,&s_wallpath }, /* {&s_lowgrduse1,&s_highgrduse1,&s_opuse1,&s_strikeuse1, &s_blitzuse,&s_enforceruse1,NULL,NULL, NULL,&s_darianuse1,NULL,NULL, NULL,NULL,NULL,NULL},*/ {0}, { &s_lowgrdshoot1,&s_highgrdshoot1,&s_opshoot1,&s_strikeshoot1, &s_blitzshoot1,&s_enforcershoot1,NULL,&s_firemonkcast1, &s_robogrdshoot1,&s_darianshoot1,&s_heinrichshoot1,NULL, NULL,NULL,&s_gunfire1,&s_wallshoot }, { &s_lowgrddie1,&s_highgrddie1,&s_opdie1,&s_strikedie1, &s_blitzdie1,&s_enforcerdie1,&s_dmonkdie1,&s_firemonkdie1, &s_robogrddie1,&s_dariandie1,&s_heinrichdie1,&s_NMEdie, &s_darkmonkdie1,NULL,&s_gundie1,NULL }, {0}, { NULL,NULL,NULL,&s_strikewait, &s_blitzstand,&s_enforcerdie1,&s_dmonkdie1,&s_firemonkdie1, &s_robogrddie1,&s_dariandie1,&s_heinrichdie1,NULL, &s_darkmonkdie1,NULL,NULL,NULL }, { &s_lowgrdcrushed1,&s_highgrdcrushed1,&s_opcrushed1,&s_strikecrushed1, &s_blitzcrushed1,&s_enforcercrushed1,&s_dmonkcrushed1,&s_firemonkcrushed1, &s_robogrddie1,NULL,NULL,NULL, NULL,NULL,NULL,NULL } }; #else static statetype * UPDATE_STATES[NUMSTATES][NUMENEMIES] = { { &s_lowgrdstand,&s_highgrdstand,NULL,&s_strikestand, &s_blitzstand,&s_enforcerstand,NULL,NULL, &s_robogrdstand,NULL,NULL,NULL, NULL,NULL,NULL,NULL }, { &s_lowgrdpath1,&s_highgrdpath1,NULL,&s_strikepath1, &s_blitzpath1,&s_enforcerpath1,NULL,NULL, &s_robogrdpath1,NULL,NULL,NULL, NULL,NULL,NULL,NULL }, { &s_lowgrdcollide,&s_highgrdcollide,NULL,&s_strikecollide, &s_blitzcollide,&s_enforcercollide,NULL,NULL, NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL }, { &s_lowgrdcollide2,&s_highgrdcollide2,NULL,&s_strikecollide2, &s_blitzcollide2,&s_enforcercollide2,NULL,NULL, &s_robogrdcollide2,NULL,NULL,NULL, NULL,NULL,NULL,NULL }, { &s_lowgrdchase1,&s_highgrdchase1,NULL,&s_strikechase1, &s_blitzchase1,&s_enforcerchase1,NULL,NULL, NULL,NULL,NULL,NULL, NULL,NULL,NULL,NULL }, /* {&s_lowgrduse1,&s_highgrduse1,&s_opuse1,&s_strikeuse1, &s_blitzuse,&s_enforceruse1,NULL,NULL, NULL,&s_darianuse1,NULL,NULL, NULL,NULL,NULL,NULL},*/ {0}, { &s_lowgrdshoot1,&s_highgrdshoot1,NULL,&s_strikeshoot1, &s_blitzshoot1,&s_enforcershoot1,NULL,NULL, &s_robogrdshoot1,NULL,NULL,NULL, NULL,NULL,NULL,NULL }, { &s_lowgrddie1,&s_highgrddie1,NULL,&s_strikedie1, &s_blitzdie1,&s_enforcerdie1,NULL,NULL, &s_robogrddie1,NULL,NULL,NULL, NULL,NULL,NULL,NULL }, {0}, { NULL,NULL,NULL,&s_strikewait, &s_blitzstand,&s_enforcerdie1,NULL,NULL, &s_robogrddie1,NULL,NULL,NULL, NULL,NULL,NULL,NULL }, { &s_lowgrdcrushed1,&s_highgrdcrushed1,NULL,&s_strikecrushed1, &s_blitzcrushed1,&s_enforcercrushed1,NULL,NULL, &s_robogrddie1,NULL,NULL,NULL, NULL,NULL,NULL,NULL } }; #endif #define TABLE_ACTOR(ob) ((ob->obclass >= lowguardobj) && (ob->obclass <= wallopobj)) void T_Reset(objtype*ob); void ApplyGravity(objtype *ob); void BeginEnemyHurt(objtype *ob); void T_PlayDead(objtype *ob); void SpawnFirewall(objtype*ob,int which,int newz); void SelectKristChaseDir(objtype*ob); void ExplodeStatic(statobj_t*tempstat); void AvoidPlayerMissile(objtype*ob); int EnvironmentDamage(objtype *ob); static int STOPSPEED = 0x200; static int PLAYERFRICTION = 0xe000; static int ACTORFRICTION = 0xf000; static int DIAGADJUST = 0xb504; static boolean MissileSound = true; boolean FirstExplosionState(statetype *state) { if (DoPanicMapping()) { if (state == &s_altexplosion1) return true; else return false; } else { if ((state == &s_explosion1) || (state == &s_grexplosion1) || (state == &s_staticexplosion1) ) return true; else return false; } } void SetGibSpeed(int speed) { MISCVARS->gibspeed = speed; } void ResetGibSpeed(void) { MISCVARS->gibspeed = NORMALGIBSPEED; } int ValidAreanumber (int areanumber) { if ((areanumber >=0) && (areanumber <= NUMAREAS)) return 1; return 0; } int GetIndexForState (statetype * state) { int i; if (state == NULL) return -1; for (i=0; i<MAXSTATES; i++) { if (statetable[i]==state) return i; } Error("Cannot find the state in 'GetIndexForState', state->shapenum = %d\n",state->shapenum); return -1; } statetype * GetStateForIndex (int index) { if (index == -1) return NULL; return statetable[index]; } statobj_t* GetStaticForIndex(int index) { statobj_t* temp; for(temp=FIRSTSTAT; temp; temp=temp->statnext) if (index == temp->whichstat) return temp; Error("Cannot find the static in 'GetStaticForIndex', statindex %d\n",index); return NULL; } void SaveActors(byte **buffer,int*size) { objtype*temp,*tact; saved_actor_type dummy; byte*tptr; int actorcount; for(actorcount=0,temp=FIRSTACTOR; temp; temp=temp->next) temp->whichactor = actorcount++; *size = sizeof(int) + sizeof(numplayers) + sizeof(misc_stuff) + objcount*sizeof(saved_actor_type); *buffer = (byte*)SafeMalloc(*size); tptr = *buffer; memcpy(tptr,MISCVARS,sizeof(misc_stuff)); tptr += sizeof(misc_stuff); memcpy(tptr,&numplayers,sizeof(numplayers)); tptr += sizeof(numplayers); memcpy(tptr,&consoleplayer,sizeof(consoleplayer)); tptr += sizeof(consoleplayer); for(temp=FIRSTACTOR; temp; temp=temp->next) { dummy.x = temp->x; dummy.y = temp->y; dummy.z = temp->z; dummy.flags = temp->flags; dummy.areanumber = temp->areanumber; //dummy.whichactor = temp->whichactor; dummy.hitpoints = temp->hitpoints; dummy.ticcount = temp->ticcount; dummy.obclass = (byte)(temp->obclass); dummy.stateindex = GetIndexForState(temp->state); dummy.shapeoffset = temp->shapeoffset; dummy.dirchoosetime = temp->dirchoosetime; dummy.door_to_open = temp->door_to_open; dummy.targetx = temp->targettilex; dummy.targety = temp->targettiley; dummy.dir = (signed char)temp->dir; dummy.angle = temp->angle; dummy.yzangle = temp->yzangle; dummy.speed = temp->speed; dummy.momentumx = temp->momentumx; dummy.momentumy = temp->momentumy; dummy.momentumz = temp->momentumz; dummy.temp1 = temp->temp1; dummy.temp2 = temp->temp2; dummy.temp3 = temp->temp3; if (temp->whatever) { /*if ((temp->flags & FL_USE) && (temp!=player)) {dummy.whateverindex = (GetIndexForState((statetype*)(temp->whatever))|SG_PSTATE); if ((dummy.whateverindex < 0) && (dummy.whateverindex != -1)) Error("Bad actor whatever save value of %d\n",dummy.whateverindex); } else*/ { tact = (objtype*)(temp->whatever); if (tact->which == ACTOR) dummy.whateverindex = tact->whichactor; else { statobj_t *tstat; tstat = (statobj_t*)(temp->whatever); dummy.whateverindex = (tstat->whichstat|SG_PSTAT); } } } else dummy.whateverindex = -1; if (temp->target) { tact = (objtype*)(temp->target); if (tact->which == ACTOR) { dummy.targetindex = tact->whichactor; Debug("\nsave actor %d, type %d has target %d",temp->whichactor,temp->obclass,tact->whichactor); } else if (tact->which == SPRITE) { statobj_t *tstat; tstat = (statobj_t*)(temp->target); dummy.targetindex = (tstat->whichstat|SG_PSTAT); } else // It must be a push wall, and we don't save that dummy.targetindex=-1; } else dummy.targetindex = -1; memcpy(tptr,&(dummy.x),sizeof(saved_actor_type)); tptr += sizeof(saved_actor_type); } } void LoadActors(byte *buffer,int size) { int numactors,i,playerindex; saved_actor_type dummy; objtype *temp; short *targetindices,*whateverindices; InitActorList(); memcpy(MISCVARS,buffer,sizeof(misc_stuff)); buffer += sizeof(misc_stuff); memcpy(&numplayers,buffer,sizeof(numplayers)); buffer += sizeof(numplayers); memcpy(&playerindex,buffer,sizeof(playerindex)); buffer += sizeof(playerindex); size -= (sizeof(misc_stuff)+sizeof(numplayers)+sizeof(playerindex)); numactors = size/sizeof(saved_actor_type); objlist = (objtype**)SafeMalloc(numactors*sizeof(objtype*)); targetindices = (short*)SafeMalloc(numactors*sizeof(short)); whateverindices = (short*)SafeMalloc(numactors*sizeof(short)); for(i=0; i<numactors; i++) { targetindices[i] = 0; whateverindices[i] = 0; objlist[i] = NULL; } for(i=0; i<numactors; i++) { GetNewActor(); objlist[i] = new; if (i < numplayers) { PLAYER[i]=new; if (i==playerindex) player=new; } memcpy(&(dummy.x),buffer,sizeof(saved_actor_type)); //new->x = dummy.x; //new->y = dummy.y; SetFinePosition(new,dummy.x,dummy.y); SetVisiblePosition(new,dummy.x,dummy.y); new->z = dummy.z; new->flags = dummy.flags; new->hitpoints = dummy.hitpoints; new->ticcount = dummy.ticcount; new->shapeoffset = dummy.shapeoffset; new->obclass = (classtype)(dummy.obclass); new->state = GetStateForIndex(dummy.stateindex); if (new->state == &s_superparticles) PARTICLE_GENERATOR = new; else if (new->state->think == T_SlideDownScreen) SCREENEYE = new; new->dirchoosetime = dummy.dirchoosetime; new->door_to_open = dummy.door_to_open; new->targettilex = dummy.targetx; new->targettiley = dummy.targety; new->dir = (dirtype)(dummy.dir); new->angle = dummy.angle; new->yzangle = dummy.yzangle; new->speed = dummy.speed; new->momentumx = dummy.momentumx; new->momentumy = dummy.momentumy; new->momentumz = dummy.momentumz; new->temp1 = dummy.temp1; new->temp2 = dummy.temp2; new->temp3 = dummy.temp3; if (dummy.whateverindex == -1) new->whatever = NULL; else if (dummy.whateverindex & SG_PSTAT) new->whatever = GetStaticForIndex(dummy.whateverindex & ~SG_PSTAT); else whateverindices[i] = dummy.whateverindex+1; if (dummy.targetindex == -1) new->target = NULL; else if (dummy.targetindex & SG_PSTAT) new->target = GetStaticForIndex(dummy.targetindex & ~SG_PSTAT); else { targetindices[i] = dummy.targetindex+1; Debug("\nload actor %d, type %d has target %d",i,new->obclass,dummy.targetindex); } new->areanumber = dummy.areanumber; new->shapenum = new->state->shapenum + new->shapeoffset; new->which = ACTOR; if (new->flags & FL_ABP) MakeActive(new); if (new->obclass != inertobj) MakeLastInArea(new); if (!(new->flags & (FL_NEVERMARK|FL_NONMARK))) actorat[new->tilex][new->tiley] = new; PreCacheActor(new->obclass,-1); buffer += sizeof(saved_actor_type); } // find unique links between actors, // searching list AFTER all have been spawned for(i=0; i<numactors; i++) { temp=objlist[i]; if (whateverindices[i]) temp->whatever = objlist[whateverindices[i]-1]; if (targetindices[i]) temp->target = objlist[targetindices[i]-1]; } for(temp=FIRSTACTOR; temp; temp=temp->next) { if (temp->obclass == b_darksnakeobj) { if (!SNAKEHEAD) SNAKEHEAD = temp; else if (!temp->whatever) SNAKEEND = temp; } } if (SNAKEHEAD) for(temp=FIRSTACTOR; temp; temp=temp->next) { if (temp->state == &s_megaexplosions) EXPLOSIONS = temp; } //SafeFree(objlist); SafeFree(targetindices); SafeFree(whateverindices); } int RandomSign(void) { if (GameRandomNumber("random sign",0) < 128) return -1; return 1; } void AddToFreeList(objtype*ob) { if (!FIRSTFREE) FIRSTFREE = ob; else { ob->prev = LASTFREE; LASTFREE->next = ob; } LASTFREE = ob; } void RemoveFromFreeList(objtype*ob) { if (ob == LASTFREE) LASTFREE = ob->prev; else ob->next->prev = ob->prev; if (ob == FIRSTFREE) FIRSTFREE = ob->next; else ob->prev->next = ob->next; ob->prev = NULL; ob->next = NULL; } void MakeActive(objtype *ob) { if ((ob == firstactive) || (ob->prevactive) || (ob->nextactive)) { SoftError("\ndouble make active try"); //AddEndGameCommand (); return; } if (!firstactive) firstactive = ob; else { ob->prevactive = lastactive; lastactive->nextactive = ob; } lastactive = ob; } void MakeLastInArea(objtype *ob) { if (!ValidAreanumber(ob->areanumber)) Error("\n ob type %s at %d,%d has illegal areanumber of %d", debugstr[ob->obclass],ob->tilex,ob->tiley,ob->areanumber); if ((ob == firstareaactor[ob->areanumber]) || (ob->previnarea) || (ob->nextinarea)) { SoftError("\ndouble make last in area try"); //AddEndGameCommand (); return; } if (!firstareaactor[ob->areanumber]) firstareaactor[ob->areanumber] = ob; else { ob->previnarea = lastareaactor[ob->areanumber]; lastareaactor[ob->areanumber]->nextinarea = ob; } lastareaactor[ob->areanumber] = ob; } void RemoveFromArea(objtype*ob) { if (!((ob == firstareaactor[ob->areanumber]) || (ob->previnarea) || (ob->nextinarea))) { SoftError("\ndouble remove from area try"); //AddEndGameCommand (); return; } if (ob == lastareaactor[ob->areanumber]) // remove from master list lastareaactor[ob->areanumber] = ob->previnarea; else ob->nextinarea->previnarea = ob->previnarea; if (ob == firstareaactor[ob->areanumber]) firstareaactor[ob->areanumber] = ob->nextinarea; else ob->previnarea->nextinarea = ob->nextinarea; ob->previnarea = NULL; ob->nextinarea = NULL; } void MakeInactive(objtype*ob) { if (!ACTIVE(ob)) // if (!((ob == firstactive) || (ob->prevactive) || (ob->nextactive))) { SoftError("\n trying to remove inactive object"); //AddEndGameCommand (); return; } //if (ob->flags & FL_ABP) { if (ob == lastactive) // remove from master list lastactive = ob->prevactive; else ob->nextactive->prevactive = ob->prevactive; if (ob == firstactive) firstactive = ob->nextactive; else ob->prevactive->nextactive = ob->nextactive; ob->prevactive = NULL; ob->nextactive = NULL; } } void A_Steal(objtype*ob) { int dx,dy,dz; ActorMovement(ob); dx = abs(ob->x - PLAYER[0]->x); dy = abs(ob->y - PLAYER[0]->y); dz = abs(ob->z - PLAYER[0]->z); if ((dx > TOUCHDIST) || (dy > TOUCHDIST) || (dz > (TOUCHDIST >> 10))) { NewState(ob,&s_blitzchase1); return; } if (ob->ticcount) return; //"Gimme That!" SD_PlaySoundRTP(SD_BLITZSTEALSND,ob->x,ob->y); if (PLAYER[0]->flags & FL_GASMASK) { PLAYER[0]->flags &= ~FL_GASMASK; PLAYERSTATE[0].protectiontime = 1; ob->temp3 = stat_gasmask; GM_UpdateBonus (PLAYERSTATE[0].poweruptime, true); } else if(PLAYER[0]->flags & FL_BPV) { PLAYER[0]->flags &= ~FL_BPV; PLAYERSTATE[0].protectiontime = 1; ob->temp3 = stat_bulletproof; GM_UpdateBonus (PLAYERSTATE[0].poweruptime, true); } else if(PLAYER[0]->flags & FL_AV) { PLAYER[0]->flags &= ~FL_AV; PLAYERSTATE[0].protectiontime = 1; ob->temp3 = stat_asbesto; GM_UpdateBonus (PLAYERSTATE[0].poweruptime, true); } else if (PLAYERSTATE[0].missileweapon != -1) { NewState(PLAYER[0],&s_player); PLAYERSTATE[0].attackframe = PLAYERSTATE[0].weaponframe = 0; PLAYERSTATE[0].new_weapon = PLAYERSTATE[0].bulletweapon; ob->temp3 = GetItemForWeapon(PLAYERSTATE[0].missileweapon); ob->temp2 = PLAYERSTATE[0].ammo; //ob->temp1 = oldpolltime; PLAYERSTATE[0].ammo = -1; if (PLAYERSTATE[0].weapon == PLAYERSTATE[0].missileweapon) PLAYERSTATE[0].weapondowntics = WEAPONS[PLAYERSTATE[0].weapon].screenheight/GMOVE; PLAYERSTATE[0].missileweapon = -1; if ( SHOW_BOTTOM_STATUS_BAR() ) DrawBarAmmo (false); } } void FindAddresses(void) { int i; unsigned long tstate,tfunct; MINFUNCTION = -1l; MAXFUNCTION = 0x00000000; MINSTATE = -1l; MAXSTATE = 0x00000000; for(i=0; i<MAXSTATES; i++) { tstate = (unsigned long)(statetable[i]); if (tstate < MINSTATE) MINSTATE = tstate; if (tstate > MAXSTATE) MAXSTATE = tstate; if (statetable[i]!=NULL) { tfunct = (unsigned long)(statetable[i]->think); if (tfunct < MINFUNCTION) MINFUNCTION = tfunct; if (tfunct > MAXFUNCTION) MAXFUNCTION = tfunct; } } } void CheckBounds(objtype*ob) { unsigned long tstate,tfunct; tstate = (unsigned long)(ob->state); tfunct = (unsigned long)(ob->state->think); if ((tfunct < MINFUNCTION) || (tfunct > MAXFUNCTION) || (tstate < MINSTATE) || (tstate > MAXSTATE)) { if (tfunct < MINFUNCTION) Error("%s has thinking function less than MINFUNCTION",debugstr[ob->obclass]); else if (tfunct > MAXFUNCTION) Error("%s has thinking function greater than MAXFUNCTION",debugstr[ob->obclass]); if (tstate < MINSTATE) Error("%s has state less than MINSTATE",debugstr[ob->obclass]); else if (tstate > MAXSTATE) Error("%s has state greater than MAXSTATE",debugstr[ob->obclass]); } } /*************************************************************/ /* ===================== = = DoActor = ===================== */ void DoActor (objtype *ob) { void (*think)(objtype *); int door; // for(i=0;i<tics;i++) // { #if (BNACRASHPREVENT == 1)// if (ob->state == 0) { return; } #endif ApplyGravity(ob); M_CheckDoor(ob); M_CheckBossSounds(ob); if ((ob->obclass >= b_darianobj) && (ob->obclass < b_darksnakeobj) && MISCVARS->REDTIME ) { MISCVARS->REDTIME --; MISCVARS->redindex = (MISCVARS->REDTIME & 15); } if (ob->obclass == playerobj) ControlPlayerObj(ob); think = ob->state->think; if (think) { //CheckBounds(ob); think (ob); if (!ob->state) { RemoveObj (ob); return; } } if (ob->ticcount) ob->ticcount --; else { if (!(ob->state->next)) { RemoveObj (ob); return; } else NewState(ob,ob->state->next); } if (ob->flags&FL_NEVERMARK) return; if ((ob->flags&FL_NONMARK) && actorat[ob->tilex][ob->tiley]) return; actorat[ob->tilex][ob->tiley] = ob; } void ApplyGravity(objtype *ob) { int oldmomentumz; if (((ob->momentumz) || (ob->z != nominalheight)) && (ob->obclass > playerobj) && ((ob->obclass <= roboguardobj) || (ob->obclass == collectorobj) || (ob->obclass == b_heinrichobj)) && (ob->state->think != T_Stand) ) { ob->z += (ob->momentumz>>16); ob->momentumz += GRAVITY; if (ob->z >= nominalheight) { ob->z = nominalheight; oldmomentumz = ob->momentumz; ob->momentumz = 0; if (oldmomentumz > 2*GRAVITY) { if (ob->flags & FL_DYING) SD_PlaySoundRTP(ACTORTHUDSND,ob->x,ob->y); else { int oldviolence = gamestate.violence; SD_PlaySoundRTP(ACTORLANDSND,ob->x,ob->y); gamestate.violence = vl_low; BeginEnemyHurt(ob); gamestate.violence = oldviolence; } } if (ob->flags&FL_FALLINGOBJECT) { RemoveObj(ob); return; } } } } /* =================== = = NewState = = Changes ob to a new state, setting ticcount to the max for that state = =================== */ void NewState (objtype *ob, statetype *newstate) { if (DoPanicMapping() && ((newstate == &s_explosion1) || (newstate == &s_grexplosion1) || (newstate == &s_staticexplosion1) ) ) ob->state = &s_altexplosion1; else { #if (BNACRASHPREVENT == 1)//crashed here when oscuro and larves were all killed if (ob == 0) { return; } #endif ob->state = newstate; } SetVisiblePosition(ob,ob->x,ob->y); #if (BNACRASHPREVENT == 1) if (ob->state == 0) { return; } #endif ob->ticcount = (ob->state->tictime>>1); ob->shapenum = ob->state->shapenum + ob->shapeoffset; } /* ========================= = = InitActorList = = Call to clear out the actor object lists returning them all to the free = list. Allocates a special spot for the player. = ========================= */ void InitActorList (void) { //====== NETWORK STUFF ======================================= memset(&DEADPLAYER[0],0,sizeof(DEADPLAYER)); NUMDEAD = 0; //======= NULLIFY GLOBAL POINTERS ============================ LASTACTOR=FIRSTACTOR=NULL; FIRSTFREE = LASTFREE = NULL; firstactive = lastactive = NULL; memset(firstareaactor,0,sizeof(firstareaactor)); memset(lastareaactor,0,sizeof(lastareaactor)); NUMSPAWNLOCATIONS = 0; PARTICLE_GENERATOR = NULL; EXPLOSIONS = NULL; SNAKEEND=SNAKEHEAD=NULL; SCREENEYE = NULL; PLAYER0MISSILE = NULL; //============================================================ objcount = 0; memset(MISCVARS,0,sizeof(misc_stuff)); MISCVARS->gibgravity = -1; MISCVARS->gibspeed = NORMALGIBSPEED; memset(&RANDOMACTORTYPE[0],0,sizeof(RANDOMACTORTYPE)); FindAddresses(); MissileSound = true; Masterdisk = false; } //=========================================================================== /* ========================= = = GetNewActor = = Sets the global variable new to point to a free spot in objlist. = The free spot is inserted at the end of the liked list = = When the object list is full, the caller can either have it bomb out ot = return a dummy object pointer that will never get used = ========================= */ void GetNewActor (void) { objtype *temp; if (!FIRSTFREE) { temp = (objtype*)Z_LevelMalloc(sizeof(objtype),PU_LEVELSTRUCT,NULL); //SoftError("\nMalloc-ing actor"); //if (insetupgame) // SoftError("in setup"); } else { temp = LASTFREE; //SoftError("\nfree actor available"); RemoveFromFreeList(LASTFREE); } if (temp) { new = temp; memset(new,0,sizeof(*new)); if (FIRSTACTOR) { new->prev = LASTACTOR; LASTACTOR->next = new; } else FIRSTACTOR = new; LASTACTOR = new; new->door_to_open = -1; new->soundhandle = -1; objcount ++; } else Error("Z_LevelMalloc failed in GetNewActor"); } //=========================================================================== /* ========================= = = RemoveObj = = Add the given object back into the free list, and unlink it from it's = neighbors = ========================= */ void RemoveObj (objtype *gone) { if (gone == PLAYER[0]) Error ("RemoveObj: Tried to remove the player!"); gone->state=NULL; MakeInactive(gone); if (gone->obclass!=inertobj) { if (ValidAreanumber(gone->areanumber)) RemoveFromArea(gone); else Error("tried to remove an instance of %s with invalid areanumber %d",debugstr[gone->obclass],gone->areanumber); } if (gone == LASTACTOR) LASTACTOR = gone->prev; else gone->next->prev = gone->prev; if (gone == FIRSTACTOR) FIRSTACTOR = gone->next; else gone->prev->next = gone->next; if (gone == EXPLOSIONS) EXPLOSIONS = NULL; gone->next = NULL; gone->prev = NULL; // SoftError("\nremoving instance of %s",debugstr[gone->obclass]); if (actorat[gone->tilex][gone->tiley] == (void*)gone) actorat[gone->tilex][gone->tiley] = NULL; gone->flags |= FL_NEVERMARK; if (gone->flags & FL_TARGET) UnTargetActor(gone); //Add_To_Delete_Array(gone); //Z_Free(gone); AddToFreeList(gone); objcount--; } //============== World Physics Model Functions ========================= void ParseMomentum(objtype *ob,int angle) { ob->momentumx += FixedMul(ob->speed,costable[angle]); ob->momentumy -= FixedMul(ob->speed,sintable[angle]); } void Set_3D_Momenta(objtype *ob, int speed, int theta, int phi) { int _2Ddiag; ob->momentumz = -FixedMul(speed,sintable[phi]); _2Ddiag = FixedMul(speed,costable[phi]); ob->momentumx = FixedMul(_2Ddiag,costable[theta]); ob->momentumy = -FixedMul(_2Ddiag,sintable[theta]); } int AngleBetween(objtype *source,objtype*target) { int dx,dy; dx = target->x - source->x; dy = source->y - target->y; return (atan2_appx(dx,dy)); } void GetMomenta(objtype *target, objtype *source, int *newmomx, int *newmomy, int *newmomz, int magnitude ) { int angle,dx,dy,dz,yzangle,xydist,_2Ddiag; dx = target->x - source->x; dy = source->y - target->y; dz = source->z - target->z; xydist = FindDistance(dx,dy); angle = atan2_appx(dx,dy); yzangle = atan2_appx(xydist,(dz<<10)); _2Ddiag = FixedMul(magnitude,costable[yzangle]); *newmomz = -FixedMul(magnitude,sintable[yzangle]); *newmomx = FixedMul(_2Ddiag,costable[angle]); *newmomy = -FixedMul(_2Ddiag,sintable[angle]); } //======================================================================= void SpawnNewObj (unsigned tilex, unsigned tiley, statetype *state, classtype which) { int newarea; GetNewActor (); new->obclass = which; SetTilePosition(new,tilex,tiley); SetVisiblePosition(new,new->x,new->y); new->dir = nodir; new->which = ACTOR; if (FirstExplosionState(state)) new->flags |= (FL_NEVERMARK|FL_NOFRICTION); if ((which != inertobj) && (which != diskobj)) actorat[tilex][tiley] = new; newarea = AREANUMBER(tilex,tiley); if ((which <= springobj) && (which != inertobj)) { if (ValidAreanumber(newarea)) new->areanumber = newarea; else Error("illegal initial areanumber of %d for actor type %s" "trying to spawn at %d, %d",newarea,debugstr[which],tilex,tiley); } else new->areanumber = newarea; if ((which != inertobj) && (!Masterdisk)) MakeLastInArea(new); NewState(new,state); new->z = nominalheight; if (which==springobj) new->z+=2; } //==================================================================== void ConsiderAlternateActor(objtype *ob,classtype which) { if (((which >= lowguardobj) && (which <= blitzguardobj)) || (which == dfiremonkobj)) { if (GameRandomNumber("SpawnStand",which) < 128) { switch(which) { case lowguardobj: ob->shapeoffset = W_GetNumForName("MARSHOO1") - W_GetNumForName("LWGSHOO1"); break; case highguardobj: ob->shapeoffset = W_GetNumForName("HIGSHOO1") - W_GetNumForName("HG2SHOO1"); break; case overpatrolobj: ob->shapeoffset = W_GetNumForName("PATSHOO1") - W_GetNumForName("OBPSHOO1"); break; case strikeguardobj: ob->shapeoffset = W_GetNumForName("XYGSHOO1") - W_GetNumForName("ANGSHOO1"); break; /*case blitzguardobj: altstartlabel = "WIGSHOO1"; new->shapeoffset = 80; break;*/ case dfiremonkobj: ob->shapeoffset = W_GetNumForName("MRKKSH1") - W_GetNumForName("ALLKSH1"); break; default: ; } } } //if (new->shapeoffset) // {if (W_CheckNumForName(altstartlabel) == -1) // new->shapeoffset = 0; // } } /* =================== = = StandardEnemyInit = =================== */ void StandardEnemyInit(objtype *ob,int dir) { int zoffset; if ((ob->obclass == deathmonkobj) || (ob->obclass == dfiremonkobj)) ob->temp2 = DRAINTIME; else if ((ob->obclass == highguardobj) || (ob->obclass == triadenforcerobj)) ob->flags |= FL_HASAUTO; ob->hitpoints = starthitpoints[gamestate.difficulty][ob->obclass]; ob->dir = dir*2; ob->flags |= (FL_SHOOTABLE|FL_BLOCK); ob->speed = ENEMYRUNSPEED; ob->dirchoosetime = 0; ob->door_to_open = -1; zoffset = MAPSPOT(ob->tilex,ob->tiley,2); if ((zoffset&0xff00)==0xb000) Set_NewZ_to_MapValue(&(ob->z),zoffset,"standard enemy",ob->tilex,ob->tiley); else ob->z = PlatformHeight(ob->tilex,ob->tiley); } //LT added //if under ext actor options BLITZ RANDOM WEP is enabled, this will decide what missile weapon a blitzguard will get void OutfitBlitzguardWith(objtype *ob) { int number = GameRandomNumber("outfitting blitzguard",0); srand((unsigned) number); number = rand() % 400; if (number < 100) { ob->temp3 = stat_bazooka; ob->temp2 = 3; } else if (number > 100 && number <= 150) { ob->temp3 = stat_heatseeker; ob->temp2 = 3; } else if (number > 150 && number <= 200) { ob->temp3 = stat_drunkmissile; ob->temp2 = 3; } else if (number > 200 && number <= 250) { ob->temp3 = stat_firewall; ob->temp2 = 3; } else if (number > 250 && number <= 300) { ob->temp3 = stat_firebomb; ob->temp2 = 3; } #if (SHAREWARE == 0) else if (number > 300 && number <= 350) { //excalibat ob->temp3 = stat_bat; ob->temp2 = 3; } else if (number > 350) { //dark staff ob->temp3 = stat_kes; ob->temp2 = 3; } #endif } extern boolean allowBlitzMoreMissileWeps; //This decides if a Blitzguard (green dude) gets a rocket launcher void ConsiderOutfittingBlitzguard(objtype *ob) { //WILEYBLITZCHANCE is defined to be 20 if ((GameRandomNumber("wiley blitzguard",0) < WILEYBLITZCHANCE) && (gamestate.difficulty >= gd_medium) ) { if (allowBlitzMoreMissileWeps) { OutfitBlitzguardWith(ob); } else { ob->temp3 = stat_bazooka; ob->temp2 = 3; } } } void BlitzBatAttack(objtype*ob, objtype*target) { //objtype *grenadetarget; int dx,dy,dz,angle,momx,momy,op; if (target->flags & FL_DYING) return; dx = abs(target->x - ob->x); dy = abs(target->y - ob->y); dz = abs(target->z - ob->z); if ((dx > 0x10000) || (dy > 0x10000) || (dz > 20)) return; SD_PlaySoundRTP(SD_EXCALISWINGSND,ob->x,ob->y); //magangle = abs(ob->angle - AngleBetween(ob,target)); //if (magangle > VANG180) //magangle = ANGLES - magangle; //if (magangle > ANGLES/8) //return; angle= ob->angle+ANGLES/16; Fix(angle); //if (temp->obclass != grenadeobj) momx = FixedMul(0x3000l,costable[angle]); momy = -FixedMul(0x3000l,sintable[angle]); if (levelheight > 2) { op = FixedMul(GRAVITY,(maxheight-100)<<16) << 1; target->momentumz = -FixedSqrtHP(op); } target->flags |= FL_NOFRICTION; SD_PlaySoundRTP(SD_EXCALIHITSND,ob->x,ob->y); if ((gamestate.violence == vl_excessive) && (GameRandomNumber("Bat Gibs",0) < 150)) { target->flags |= FL_HBM; DamageThing(target,50); } else DamageThing(target,10); if ((target->flags & FL_HBM) && (target->hitpoints > 0)) target->flags &= ~FL_HBM; Collision(target,ob,momx,momy); } /* =============== = = SpawnStand = =============== */ void SpawnStand (classtype which, int tilex, int tiley, int dir, int ambush) { statetype *temp; #if (SHAREWARE == 1) switch(which) { case overpatrolobj: case wallopobj: case deathmonkobj: case dfiremonkobj: case b_darianobj: case b_heinrichobj: case b_darkmonkobj: Error("\n%s actor at %d,%d not allowed in shareware !",debugstr[which],tilex,tiley); break; default: ; } #endif if ((which == lowguardobj) && (GameRandomNumber("SpawnStand",which) < 128)) which = blitzguardobj; if ((temp = UPDATE_STATES[STAND][which-lowguardobj]) != NULL) { SpawnNewObj(tilex,tiley,temp,which); if (!loadedgame) gamestate.killtotal++; if (ambush) new->flags |= FL_AMBUSH; StandardEnemyInit(new,dir); if (which == b_darkmonkobj) { new->flags |= (FL_NOFRICTION);//|FL_INVULNERABLE); new->speed = ENEMYRUNSPEED*2; } if (which == blitzguardobj) ConsiderOutfittingBlitzguard(new); if ((new->obclass >= lowguardobj) && (new->obclass <= dfiremonkobj)) RANDOMACTORTYPE[new->obclass]++; if (MAPSPOT(tilex,tiley,2) == 0xdead) { new->flags |= FL_KEYACTOR; MISCVARS->KEYACTORSLEFT++; } PreCacheActor(which,0); } //else //Error("NULL initialization error"); } /* =============== = = SpawnPatrol = =============== */ void SpawnPatrol (classtype which, int tilex, int tiley, int dir) { statetype *temp; int path=PATH; #if (SHAREWARE==1) switch(which) { case overpatrolobj: case wallopobj: case deathmonkobj: case dfiremonkobj: case b_darianobj: case b_heinrichobj: case b_darkmonkobj: Error("\n%s actor at %d,%d not allowed in shareware !",debugstr[which],tilex,tiley); break; default: ; } #endif if ((which == lowguardobj) && (GameRandomNumber("SpawnStand",which) < 128)) which = blitzguardobj; if ((temp= UPDATE_STATES[path][(int)(which-lowguardobj)]) != NULL) { SpawnNewObj(tilex,tiley,temp,which); if (!loadedgame) gamestate.killtotal++; StandardEnemyInit(new,dir); if ((which == wallopobj) || (which == roboguardobj)) { new->flags |= FL_NOFRICTION; //new->flags &= ~FL_SHOOTABLE; new->dir <<= 1; ParseMomentum(new,dirangle16[new->dir]); } else ParseMomentum(new,dirangle8[new->dir]); if (which == blitzguardobj) ConsiderOutfittingBlitzguard(new); if (MAPSPOT(tilex,tiley,2) == 0xdead) { new->flags |= FL_KEYACTOR; MISCVARS->KEYACTORSLEFT++; } PreCacheActor(which,0); } } //========================================================================== void SpawnDisk(int tilex, int tiley, int type, boolean master) { int zoffset; if (master == true) { Masterdisk = true; SpawnNewObj(tilex,tiley,&s_diskmaster,diskobj); Masterdisk = false; new->flags |= FL_MASTER; new->momentumz = -(DISKMOMZ << 16); new->flags |= FL_SYNCED; new->flags |= FL_NEVERMARK; new->temp1 = 1; //RemoveFromArea(new); } else { if (!type) { SpawnNewObj(tilex,tiley,&s_elevdisk,diskobj); new->momentumz = -(DISKMOMZ << 16); //new->flags |= FL_SYNCED; zoffset = MAPSPOT(tilex,tiley,2); if ((zoffset&0xff00)==0xb000) Set_NewZ_to_MapValue((fixed*)(&(new->temp2)),zoffset,"elev disk",tilex,tiley); else new->temp2 = 32; new->temp1 = 1; } else { SpawnNewObj(tilex,tiley,&s_pathdisk,diskobj); zoffset = MAPSPOT(tilex,tiley,2); if ((zoffset&0xff00)==0xb000) Set_NewZ_to_MapValue((fixed*)(&(new->z)),zoffset,"path disk",tilex,tiley); new->dir = (type-1) << 1; new->speed = 0x1000; //ParseMomentum(new,dirangle8[new->dir]); } actorat[tilex][tiley] = NULL; new->flags |= FL_BLOCK; new->flags |= (FL_NOFRICTION|FL_ACTIVE|FL_NEVERMARK); } } objtype* DiskAt(int tilex,int tiley) { int area; objtype *temp; statobj_t *tstat; area = AREANUMBER(tilex,tiley); for(temp = firstareaactor[area]; temp; temp = temp->nextinarea) { if ((temp->tilex != tilex) || (temp->tiley != tiley) || (temp->obclass != diskobj)) continue; return temp; } for(tstat = firstactivestat; tstat; tstat = tstat->nextactive) { if ((tstat->tilex != tilex) || (tstat->tiley != tiley) || (tstat->itemnumber != stat_disk)) continue; return (objtype*)tstat; } return NULL; } void SetElevatorDiskVariables(objtype *ob,int newz, int newmomentumz, int newtemp1,int newtemp3,int newdirchoose) { ob->z = newz; ob->momentumz = newmomentumz; ob->temp1 = newtemp1; ob->temp3 = newtemp3; ob->dirchoosetime = newdirchoose; } void T_ElevDisk(objtype*ob) { objtype *temp = (objtype*)(actorat[ob->tilex][ob->tiley]); objtype *master; if (ob->flags & FL_MASTER) goto masterlabel; master = (objtype*)(ob->target); if (!master) Error("disk without master !"); //SoftError("\n ob->z:%d %s, master z:%d",ob->z, // (ob->flags & FL_SYNCED)?("SYNCED"):("UNSYNCED"),master->z); if (M_ISACTOR(temp) && (temp != ob) && (!(temp->flags & FL_DYING))) { int dz = abs(ob->z - temp->z), dx = abs(ob->x - temp->x), dy = abs(ob->y - temp->y); if ((dx < 0x7000) && (dy < 0x7000) && (dz < 68) && (temp->z > ob->z)) { ob->flags &= ~FL_SYNCED; return; } } if (master && (!(ob->flags & FL_SYNCED))) { int dz; dz = abs(master->z - ob->z); if ((dz > 0) && (dz < 8)) { SetElevatorDiskVariables(ob,master->z,master->momentumz,master->temp1, master->temp3,master->dirchoosetime); ob->flags |= FL_SYNCED; //return; } return; } masterlabel: if (ob->dirchoosetime) { ob->dirchoosetime --; return; } if (ob->temp1) // moving { ob->z += (ob->momentumz >> 16); if (ob->momentumz > 0) // down { if (ob->z >= nominalheight + 40 + DISKMOMZ) SetElevatorDiskVariables(ob,ob->z - (ob->momentumz>>16),0,0,0,35); } else { if (ob->z < ob->temp2) // temp2 has max height SetElevatorDiskVariables(ob,ob->z - (ob->momentumz>>16),0,0,1,35); } } else { if (ob->temp3) ob->momentumz = (DISKMOMZ << 16); else ob->momentumz = -(DISKMOMZ << 16); ob->temp1 = 1; } } void SpawnInertActor(int newx,int newy, int newz) { GetNewActor (); MakeActive(new); new->obclass = inertobj; new->which = ACTOR; SetFinePosition(new,newx,newy); SetVisiblePosition(new,new->x,new->y); new->z = newz; new->dir = 0; new->speed = 0; new->flags = (FL_NEVERMARK|FL_ABP); } #if (SHAREWARE == 0) void SpawnGroundExplosion(int x, int y, int z) { SpawnInertActor(x,y,z); NewState(new,&s_grexplosion1); new->temp2 = GameRandomNumber("SpawnGroundExplosion",0)>>2; } #endif void SpawnSlowParticles(int which, int numgibs, int x,int y,int z) { objtype *prevlast,*temp; int tilex,tiley; tilex = x>>16; tiley = y>>16; SpawnNewObj(tilex,tiley,&s_gibs1,inertobj); SetFinePosition(new,x,y); SetVisiblePosition(new,x,y); prevlast = new; prevlast->flags |= FL_ABP; MakeActive(prevlast); SpawnParticles(new,which,numgibs); for(temp = prevlast->next; temp; temp=temp->next) { temp->z = z; temp->momentumx >>= 1; temp->momentumy >>= 1; temp->momentumz >>= 1; } RemoveObj(prevlast); } void ResolveDoorSpace(int tilex,int tiley) { statobj_t* tstat,*temp; for(tstat = firstactivestat; tstat;) { temp = tstat->nextactive; if (tstat->flags & FL_DEADBODY) { if ((tstat->tilex == tilex) && (tstat->tiley == tiley)) { if ((tstat->flags & FL_DEADBODY) && (tstat->linked_to != -1)) DEADPLAYER[tstat->linked_to] = NULL; RemoveStatic(tstat); if (tstat->flags & FL_DEADBODY) SpawnSlowParticles(GUTS,8,tstat->x,tstat->y,tstat->z); else SpawnSlowParticles(gt_sparks,8,tstat->x,tstat->y,tstat->z); SD_PlaySoundRTP(SD_ACTORSQUISHSND,tstat->x,tstat->y); } } tstat = temp; } } void SpawnSpear(int tilex,int tiley,int up) { int count,i; statetype *tstate; if (BATTLEMODE && (!gamestate.BattleOptions.SpawnDangers)) return; if (!up) { #if (SHAREWARE == 1) Error("\ndownspear at %d,%d in shareware!",tilex,tiley); #else SpawnNewObj(tilex,tiley,&s_speardown1,spearobj); new->z = 0; #endif } else SpawnNewObj(tilex,tiley,&s_spearup1,spearobj); count = (int)(GameRandomNumber("Spawn Spear",0) % 16); for(i=0,tstate = new->state; i<count; i++,tstate=tstate->next); NewState(new,tstate); PreCacheActor(spearobj,up); new->flags |= (FL_ABP);//|FL_INVULNERABLE); MakeActive(new); } void SpawnSpring(int tilex,int tiley) { int iconvalue; iconvalue = MAPSPOT(tilex,tiley,2); if (iconvalue == 3) { SpawnNewObj(tilex,tiley,&s_autospring1,springobj); new->ticcount = (GameRandomNumber("Spawn Spring",0) % new->ticcount)+1; new->temp1 = iconvalue; } else { SpawnNewObj(tilex,tiley,&s_spring1,springobj); if (iconvalue == 2) new->temp1 = iconvalue; } PreCacheActor(springobj,0); new->flags &= ~(FL_SHOOTABLE|FL_BLOCK); } void T_Spring(objtype*ob) { objtype *temp; int op,dx,dy,dz; if ((ob->state->condition & SF_DOWN) && (ob->temp1)) { if (ob->ticcount) return; ob->shapenum++; TurnActorIntoSprite(ob); return; } for(temp=firstareaactor[ob->areanumber]; temp; temp=temp->nextinarea) { if (temp == ob) continue; if (temp->obclass >= roboguardobj) continue; dx = abs(ob->x-temp->x); dy = abs(ob->y-temp->y); dz = abs(ob->z-temp->z); if ((dx > ACTORSIZE+0x2800) || (dy > ACTORSIZE+0x2800) || (dz > 40)) continue; if (!temp->momentumz) { op = FixedMul(GRAVITY,(temp->z-5)<<16) << 1; temp->momentumz = -FixedSqrtHP(op); SD_PlaySoundRTP(SD_SPRINGBOARDSND,ob->x,ob->y); } } } void T_Count(objtype*ob) { int index; touchplatetype *temp; objtype* tempactor; if (ob->dirchoosetime) { ob->dirchoosetime --; if (ob->dirchoosetime>980) MISCVARS->gasindex=((1050-ob->dirchoosetime)<<4)/70; else if (ob->dirchoosetime<35) MISCVARS->gasindex=(ob->dirchoosetime<<4)/35; if (ob->temp3) { ob->temp3 --; if (ob->temp3 & 1) SD_PlaySoundRTP(SD_GASHISSSND,ob->x,ob->y); } else { ob->temp3 = 105; for(tempactor=firstareaactor[ob->areanumber]; tempactor; tempactor=tempactor->nextinarea) { if (tempactor == ob) continue; if (!(tempactor->flags & FL_SHOOTABLE)) continue; if (tempactor->obclass != playerobj) { if ((tempactor->obclass >= lowguardobj) && (tempactor->obclass <= dfiremonkobj)) { int oldviolence = gamestate.violence; gamestate.violence = vl_low; DamageThing(tempactor,EnvironmentDamage(ob)); Collision(tempactor,ob,-(tempactor->momentumx),-(tempactor->momentumy)); gamestate.violence = oldviolence; } } else if (!(tempactor->flags & FL_GASMASK)) { DamageThing(tempactor,EnvironmentDamage(ob)); Collision(tempactor,ob,0,0); M_CheckPlayerKilled(tempactor); } } } } else { int i; playertype *pstate; for(i=0; i<numplayers; i++) { M_LINKSTATE(PLAYER[i],pstate); PLAYER[i]->flags &= ~FL_GASMASK; pstate->protectiontime = 1; } NewState(ob,&s_gas1); SD_PlaySoundRTP(SD_GASENDSND,ob->x,ob->y); ob->flags &= ~FL_ACTIVE; MISCVARS->gasindex=0; MU_StartSong(song_level); MU_RestoreSongPosition(); MISCVARS->GASON = 0; index = touchindices[ob->temp1][ob->temp2]-1; TRIGGER[index] = 0; for(temp = touchplate[index]; temp; temp = temp->nextaction) if (temp->action == EnableObject) { tempactor = (objtype*)(temp->whichobj); tempactor->flags &= ~FL_ACTIVE; } } } void SpawnBlade(int tilex, int tiley,int dir,int upordown,int moving) { int count,i; statetype *nstate; #if (SHAREWARE == 1) if (!upordown) Error("\ndown spinblade at %d,%d not allowed in shareware !",tilex,tiley); if (moving) Error("\nupdown spinblade at %d,%d not allowed in shareware !",tilex,tiley); #endif if (BATTLEMODE && (!gamestate.BattleOptions.SpawnDangers)) return; if (moving) { #if (SHAREWARE == 0) if (upordown) SpawnNewObj(tilex,tiley,&s_spinupblade1,bladeobj); else { SpawnNewObj(tilex,tiley,&s_spindownblade1,bladeobj); new->z = 0; } #endif } else { if (upordown) SpawnNewObj(tilex,tiley,&s_upblade1,bladeobj); #if (SHAREWARE == 0) else { SpawnNewObj(tilex,tiley,&s_downblade1,bladeobj); new->z = 0; } #endif } count = (int)(GameRandomNumber("SpawnBlade",0) % 16); for(nstate=new->state,i=0; i<count; nstate = nstate->next,i++); NewState(new,nstate); new->flags |= (FL_BLOCK); new->flags &= ~FL_SHOOTABLE; new->dir = dir; if (dir != nodir) { new->flags |= FL_NOFRICTION; new->speed = ENEMYRUNSPEED; } if (!MAPSPOT(tilex,tiley,2)) { new->flags |= FL_ACTIVE; ParseMomentum(new,dirangle8[new->dir]); } PreCacheActor(bladeobj,(moving<<1)+upordown); } void SpawnCrushingColumn(int tilex, int tiley, int upordown) { int i,count; statetype * nstate; #if (SHAREWARE == 1) if (!upordown) Error("\ncrush-up column at %d,%d not allowed in shareware!",tilex,tiley); #endif if (BATTLEMODE && (!gamestate.BattleOptions.SpawnDangers)) return; #if (SHAREWARE == 0) if (!upordown) SpawnNewObj(tilex,tiley,&s_columnupup1,crushcolobj); else #endif { SpawnNewObj(tilex,tiley,&s_columndowndown1,crushcolobj); new->z = 0; } count = (int)(GameRandomNumber("SpawnCrushingColumn",0) % 8); for(nstate=new->state,i=0; i<count; nstate = nstate->next,i++) { if ((!upordown) && (nstate->condition & SF_UP)) new->temp1 += (((nstate->tictime>>1) + 1)<<2); } NewState(new,nstate); new->flags |= (FL_BLOCK); new->flags &= ~FL_SHOOTABLE; PreCacheActor(crushcolobj,upordown); } void SpawnFirejet(int tilex, int tiley, int dir, int upordown) { int statecount,i; statetype *tstate; statecount = (int)(GameRandomNumber("SpawnFirejet",0) % 22); if (upordown) { for(i=0,tstate=&s_firejetup1; i<statecount; i++,tstate=tstate->next); SpawnNewObj(tilex,tiley,tstate,firejetobj); } else { #if (SHAREWARE == 1) Error("\ndown firejet at %d,%d not allowed in shareware",tilex,tiley); #else for(i=0,tstate=&s_firejetdown1; i<statecount; i++,tstate=tstate->next); SpawnNewObj(tilex,tiley,tstate,firejetobj); new->z = 0; #endif } PreCacheActor(firejetobj,upordown); new->flags &= ~FL_SHOOTABLE; if (dir != nodir) { new->dir = dir*2; new->flags |= FL_NOFRICTION; new->speed = ENEMYRUNSPEED; ParseMomentum(new,dirangle8[new->dir]); } else new->dir = dir; } void SpawnFirebomb(objtype*ob,int damage,int which) { int i,low,high,doorat; wall_t *tempwall; doorobj_t*tempdoor; if (which == 0) { low = (ob->dir>>1); high = low; } else { low = 0; high = which-1; if ((FindDistance((ob->x-player->x), (ob->y-player->y))<0x120000) && (player->z==nominalheight) ) SHAKETICS = 35; } for (i=low; i<=high; i++) { MissileSound = false; /* if (((which == 0) && ((low == 5) || (low == 6))) || ((which == 6) && ((i==4) || (i==5))) ) { if (((which == 0) && (low == 5)) || ((which == 6) && (i == 4)) ) { newz = ob->z + 64; if (newz > maxheight) continue; SpawnMissile(ob,p_firebombobj,0,0,&s_grexplosion1,0); new->z = newz; new->dir = 10; } else { newz = ob->z - 64; if ((sky == 0) && (newz < 0)) continue; SpawnMissile(ob,p_firebombobj,0,0,&s_grexplosion1,0); new->z = newz; new->dir = 12; } } else */ { SpawnMissile(ob,p_firebombobj,0,dirangle8[2*i],&s_grexplosion1,0x10000); new->z = ob->z; new->dir = (i<<1); } MissileSound = true; SD_PlaySoundRTP(SD_EXPLODEFLOORSND,ob->x,ob->y); new->temp2 = FixedMul(damage,DIAGADJUST); tempwall = (wall_t*)actorat[new->tilex][new->tiley]; doorat= 0; if (M_ISDOOR(new->tilex,new->tiley)) { tempdoor = doorobjlist[tilemap[new->tilex][new->tiley]&0x3ff]; if (tempdoor->position<0x8000) doorat = 1; } if ((tempwall && M_ISWALL(tempwall)) || doorat || (new->tilex <=0) || (new->tilex > MAPSIZE-1) || (new->tiley <=0) || (new->tiley > MAPSIZE-1) ) { new->z = ob->z; SetFinePosition(new,ob->x,ob->y); SetVisiblePosition(new,ob->x,ob->y); } new->whatever = ob->whatever; new->temp3 = ob->temp3 - 1; } } void MissileHitActor(objtype *owner, objtype *missile, objtype *victim, int damage, int hitmomx, int hitmomy ) { int tcl = victim->obclass; int ocl = missile->obclass; if ( (victim->flags & FL_DYING) || // hey, they're dying already; (victim->flags & FL_HEAD) || // don't hurt overrobot's head, wheels (tcl == wallopobj) || // bcraft is invulnerable (tcl == b_darkmonkobj) || // darkmonk is invulnerable (!(victim->flags & FL_SHOOTABLE)) || // don't hurt environment dangers, dead guys ((tcl == b_darksnakeobj) && ((SNAKELEVEL != 3) || (!victim->temp3))// return for non-red snake ) ) return; if ((tcl == playerobj) || (tcl == b_heinrichobj)) victim->target = owner; if (tcl == NMEsaucerobj) // can shoot over's saucer { NewState(victim,&s_explosion1); victim->flags &= ~FL_SHOOTABLE; victim->temp2 = damage; return; } else if (tcl == roboguardobj) // check roboguard { DamageThing(victim,damage); Collision(victim,owner,0,0); } else if (tcl == collectorobj) { if (gamestate.SpawnEluder) return; DamageThing(victim,damage); Collision(victim,owner,0,0); } else if (tcl == patrolgunobj) { DamageThing(victim,damage); if (victim->hitpoints <= 0) { victim->momentumx = victim->momentumy = victim->momentumz = 0; victim->flags |= FL_DYING; if (victim->temp1 == -1) // this is 4-way gun NewState(victim,&s_robogrddie1); #if (SHAREWARE == 0) else // this is normal NewState(victim,&s_gundie1); #endif } } // bosses are "special" ========================== else if ((tcl >= b_darianobj) && (tcl < b_darkmonkobj)) { DamageThing(victim,damage); if (!(victim->flags & FL_ATTACKMODE)) FirstSighting (victim); // put into combat mode if (victim->hitpoints <= 0) { victim->momentumx = victim->momentumy = victim->momentumz = 0; victim->flags |= FL_DYING; NewState(victim,UPDATE_STATES[DIE][victim->obclass-lowguardobj]); switch (victim->obclass) { case b_darianobj: AddMessage("Darian defeated!",MSG_CHEAT); break; case b_heinrichobj: AddMessage("Krist defeated!",MSG_CHEAT); break; case b_robobossobj: AddMessage("NME defeated!",MSG_CHEAT); break; default: ; } MU_StartSong(song_bossdie); } #if (SHAREWARE == 0) else { MISCVARS->REDTIME = (damage >> 1); if (victim->obclass == b_heinrichobj) { NewState(victim,&s_heinrichdefend); if (Near(victim,PLAYER[0],3)) { MISCVARS->HRAMMING = 1; MISCVARS->HMINING = 0; victim->dirchoosetime = 0; } else { MISCVARS->HMINING = 1; MISCVARS->HRAMMING = 0; victim->dirchoosetime = 5;//10; } victim->targettilex = victim->targettiley = 0; victim->target = NULL; } } #endif } #if (SHAREWARE == 0) else if ((tcl == b_darksnakeobj) && (victim->temp3)) // red snake { DamageThing(SNAKEEND,damage); if (victim->state->think == T_DarkSnakeChase) NewState(victim,&s_redheadhit); else NewState(victim,&s_redlinkhit); victim->temp3 = 0; } #endif //=============================================== else // all other actors { if ((tcl == playerobj) && (victim->flags & FL_AV) && (ocl != p_godballobj) ) { playertype *pstate; M_LINKSTATE(victim,pstate); pstate->protectiontime -= ((damage<<1) + damage); if (pstate->protectiontime < 1) pstate->protectiontime = 1; if (victim==player) GM_UpdateBonus (pstate->protectiontime, false); return; // asbestos vest prevents victim damage } DamageThing(victim,damage); if ((tcl < roboguardobj) && (victim->hitpoints <= 0)) { if (ocl != p_godballobj) victim->flags |= FL_HBM; else victim->flags |= (FL_GODSTRUCK | FL_FULLLIGHT); } if (tcl == playerobj) { playertype *pstate; M_LINKSTATE(victim,pstate); if (pstate->health <= 0) { if (ocl != p_godballobj) victim->flags |= FL_HBM; else victim->flags |= (FL_GODSTRUCK | FL_FULLLIGHT); if (M_ISACTOR(owner)) { if (owner->obclass == playerobj) { if (!victim->momentumz) BATTLE_PlayerKilledPlayer(battle_kill_with_missile,owner->dirchoosetime,victim->dirchoosetime); else BATTLE_PlayerKilledPlayer(battle_kill_with_missile_in_air,owner->dirchoosetime,victim->dirchoosetime); } else BATTLE_CheckGameStatus(battle_player_killed,missile->dirchoosetime); } else BATTLE_CheckGameStatus(battle_player_killed,missile->dirchoosetime); } } if ((owner->obclass == playerobj) && (victim->flags & FL_HBM)) { MISCVARS->supergibflag = true; //GivePoints(starthitpoints[gamestate.difficulty][victim->obclass]*5); } Collision(victim,owner,hitmomx,hitmomy); MISCVARS->supergibflag = false; if ((tcl == blitzguardobj) && (owner->obclass == playerobj)) victim->flags |= FL_TARGET; } } void MissileHit (objtype *ob,void *hitwhat) { int damage=0, random,tcl=0,ocl,sound,hitmomx,hitmomy; objtype* tempactor=NULL,*owner; if (ob==missobj) missobj=NULL; if (ob == PLAYER0MISSILE) PLAYER0MISSILE = NULL; ob->momentumz = 0; hitmomx = ob->momentumx; hitmomy = ob->momentumy; if (ob->soundhandle != -1) SD_StopSound(ob->soundhandle); ob->flags &= ~FL_SHOOTABLE; if (FirstExplosionState(ob->state)) return; /* if ((ob->z < -28) || (IsWindow(ob->tilex,ob->tiley))) { NewState(ob,&s_megaremove); return; } */ tempactor = (objtype*)hitwhat; owner = (objtype*)(ob->whatever); random = GameRandomNumber("MissileHit",0); ocl = ob->obclass; if (tempactor) { if (tempactor->which == ACTOR) tcl = tempactor->obclass; else if (tempactor->which == SPRITE) tcl = -1; } if ((!tcl) && (ob->z < -30)) { if (ob->soundhandle != -1) SD_StopSound(ob->soundhandle); NewState(ob,&s_megaremove); return; } if (((ocl != p_kesobj) && (ocl != p_godballobj)) || (!tcl)) ZEROMOM; if (tcl == b_darianobj) MISCVARS->ESAU_SHOOTING = false; switch(ocl) { case p_bazookaobj: NewState(ob,&s_explosion1); if (M_ISACTOR(owner) && (owner->obclass == blitzguardobj)) damage = 30 + (random >> 4); else damage = 2*((random>>3)+80); break; case p_heatseekobj: NewState(ob,&s_explosion1); damage = 2*((random>>3)+50); break; case p_drunkmissileobj: NewState(ob,&s_explosion1); damage = ((random>>3)+25); break; case p_firebombobj: NewState(ob,&s_explosion1); damage = 2*((random>>3)+90); ob->temp3 = 4; SpawnFirebomb(ob,damage,4); break; case p_firewallobj: if (tcl == playerobj) gamestate.DOGROUNDZEROBONUS = true; NewState(ob,&s_explosion1); damage = 2*((random>>3)+50); break; case p_godballobj: if ((tcl >= pillarobj) || (!tcl) || ((tcl == -1) && (!(tempactor->flags & FL_SHOOTABLE)))) NewState(ob,&s_explosion1); ob->target = NULL; damage = 500; break; case shurikenobj: NewState(ob,&s_explosion1); damage = ((random >>3) + 30)>>2; break; case grenadeobj: NewState(ob,&s_explosion1); damage = (random >>3) + 20; break; case fireballobj: NewState(ob,&s_explosion1); damage = (random >> 3) + 10; break; case missileobj: NewState(ob,&s_explosion1); if (M_ISACTOR(owner) && (owner->obclass == wallopobj)) damage = (random >> 5); else damage = (random >>3) + 30; if (tcl && (tcl != b_heinrichobj)) damage = 3*damage>>3; break; case wallfireobj: if ((!tempactor) || (tempactor->which == ACTOR) || (tempactor->which == SPRITE) ) NewState(ob,&s_explosion1); else if (M_ISWALL(tempactor) || (tempactor->which == DOOR)) NewState(ob,&s_crossdone1); damage = EnvironmentDamage(ob); break; case inertobj: ob->state = NULL; return; break; #if (SHAREWARE == 0) case p_splitmissileobj: NewState(ob,&s_explosion1); damage = 2*((random>>3)+50); break; case p_kesobj: if ((tcl >= pillarobj) || (!tcl) || ((tcl == -1) && (!(tempactor->flags & FL_SHOOTABLE))) ) NewState(ob,&s_explosion1); damage = 2*((random>>3)+140); break; case netobj: ob->state=NULL; MISCVARS->NET_IN_FLIGHT = false; if (tempactor == PLAYER[0]) { if ((tempactor->flags & FL_GODMODE) || (tempactor->flags & FL_DOGMODE) || godmode ) damage = 0; else { damage = (random >>4) + 5; PLAYERSTATE[0].NETCAPTURED = -1; PLAYERSTATE[0].weapondowntics = WEAPONS[PLAYERSTATE[0].weapon].screenheight/GMOVE; NewState(PLAYER[0],&s_player); PLAYERSTATE[0].attackframe = PLAYERSTATE[0].weaponframe = 0; PLAYERSTATE[0].batblast = 0; if (PLAYERSTATE[0].HASKNIFE == false) AddMessage("Wiggle left and right to get out of net!", MSG_GAME); } } break; case bigshurikenobj: NewState(ob,&s_oshurikenhit1); if (owner->obclass == wallopobj) damage = (random >> 5); else damage = 4*((random >>3) + 30)/10; break; case dm_spitobj: NewState(ob,&s_spithit1); damage = 30; if (gamestate.difficulty == gd_hard) damage += 15; break; case dm_weaponobj: damage = 20; NewState(ob,&s_explosion1); break; case dm_heatseekobj: damage = 20; NewState(ob,&s_explosion1); break; case dmfballobj: NewState(ob,&s_explosion1); damage = (random >>3) + 20; break; case h_mineobj: NewState(ob,&s_explosion1); damage = (random >>3) + 20; break; case NMEsaucerobj: NewState(ob,&s_explosion1); damage = 2*((random>>3)+30); break; #endif //default: //Error("Unknown ob %d called MissileHit",ob->obclass); } //if (!ob->state) //return; if ((sound = BAS[ob->obclass].hit)!=0) SD_PlaySoundRTP(sound,ob->x,ob->y); if (FirstExplosionState(ob->state)) SD_PlaySoundRTP(SD_EXPLODESND,ob->x,ob->y); if (tcl>0) // actors { MissileHitActor(owner,ob,tempactor,damage,hitmomx,hitmomy); if ((ocl == p_kesobj) && (tcl < roboguardobj)) { tempactor->momentumx = hitmomx; // kes gives wus targets its momentum tempactor->momentumy = hitmomy; //missile->flags |= FL_NOFRICTION; } } else if (tcl < 0) // static { DamageThing(hitwhat,damage); if (FirstExplosionState(new->state)) new->whatever = ob->whatever; } } void T_Spears(objtype*ob) { int dx,dy,dz,i; for(i=0; i<numplayers; i++) { if (PLAYER[i]->flags & FL_DYING) continue; dx = abs(PLAYER[i]->x - ob->x); dy = abs(PLAYER[i]->y - ob->y); dz = abs(PLAYER[i]->z - ob->z); if ((!ob->ticcount)&&(ob->state->condition&SF_SOUND) && areabyplayer[ob->areanumber]) SD_PlaySoundRTP(BAS[ob->obclass].operate,ob->x,ob->y); if ((dx < STANDDIST) && (dy < STANDDIST) && (dz < 20)) { ob->flags &= ~FL_BLOCK; if ((!ob->ticcount) && (ob->state->condition & SF_CRUSH)) { DamageThing(PLAYER[i],EnvironmentDamage(ob)); Collision(PLAYER[i],ob,0,0); M_CheckPlayerKilled(PLAYER[i]); return; } } else { if (ob->state->condition & SF_DOWN) ob->flags &= ~FL_BLOCK; else ob->flags |= FL_BLOCK; } } } void T_CrushUp(objtype*ob) { int dx, dy,dist,dz,i; if ((!ob->ticcount) && (ob->state->condition & SF_SOUND) && areabyplayer[ob->areanumber]) SD_PlaySoundRTP(BAS[ob->obclass].operate,ob->x,ob->y); dist = ACTORSIZE+0x2000; if (ob->state->condition & SF_UP) { ob->temp1 += 4; // Debug("\ncol momz = 4"); } else if (ob->state->condition & SF_DOWN) { ob->temp1 -= 4; // Debug("\ncol mom z = -4"); } else { //ob->momentumz = 0; // Debug("\ncol mom z = 0"); } ob->temp2 = maxheight - ob->temp1 + 32; for(i=0; i<numplayers; i++) { dx = abs(PLAYER[i]->x - ob->x); dy = abs(PLAYER[i]->y - ob->y); dz = abs(ob->temp2-PLAYER[i]->z); if ((dx < dist) && (dy < dist) && (dz < 65)) { ob->flags &= ~FL_BLOCK; //player->temp2 = 0; if ((!ob->ticcount) && (ob->state->condition & SF_CRUSH) && (levelheight<2) && (!(ob->flags & FL_DYING))) { DamageThing(PLAYER[i],EnvironmentDamage(ob)); if (PLAYER[i]->hitpoints <= 0) PLAYER[i]->flags |= FL_HBM; Collision(PLAYER[i],ob,0,0); M_CheckPlayerKilled(PLAYER[i]); //NewState(ob,ob->state); //reset ticcount return; } if (ob->state->condition & SF_UP) { PLAYER[i]->momentumz = -(4<<16); if (PLAYER[i]->z < -30) PLAYER[i]->z = -30; } else if (ob->state->condition & SF_DOWN) { PLAYER[i]->momentumz = (4<<16); if (PLAYER[i]->z >= nominalheight) PLAYER[i]->z = nominalheight; } else PLAYER[i]->momentumz = 0; PLAYER[i]->whatever = ob; ob->whatever = PLAYER[i]; //PLAYER[i]->flags |= FL_RIDING; } } if (ob->state->condition & SF_BLOCK) ob->flags |= FL_BLOCK; else ob->flags &= ~FL_BLOCK; } void T_CrushDown(objtype*ob) { int dx,dy,dz,i; if ((!ob->ticcount) && (ob->state->condition & SF_SOUND)&& areabyplayer[ob->areanumber]) SD_PlaySoundRTP(BAS[ob->obclass].operate,ob->x,ob->y); ob->temp2 = ob->z; for(i=0; i<numplayers; i++) { dx = abs(PLAYER[i]->x - ob->x); dy = abs(PLAYER[i]->y - ob->y); dz = abs(PLAYER[i]->z - ob->z); if ((dx < STANDDIST) && (dy < STANDDIST) && (dz < 20)) { //PLAYER[i]->temp2 = 0; ob->flags &= ~FL_BLOCK; if ((!ob->ticcount) && (ob->state->condition & SF_CRUSH) && (!(ob->flags & FL_DYING))) { DamageThing(PLAYER[i],EnvironmentDamage(ob)); if (PLAYER[i]->hitpoints <= 0) PLAYER[i]->flags |= FL_HBM; Collision(PLAYER[i],ob,0,0); M_CheckPlayerKilled(PLAYER[i]); //NewState(ob,ob->state); //reset ticcount return; } if ((ob->state->condition & SF_DOWN) && ((ob->state != &s_columndowndown1) && (ob->state != s_columndowndown1.next))) { PLAYER[i]->temp2 = COLUMNCRUSH; PLAYER[i]->whatever = ob; } } } if (ob->state->condition & SF_BLOCK) ob->flags |= FL_BLOCK; else ob->flags &= ~FL_BLOCK; } void T_Explosion(objtype* ob) { int momx,momy,momz; int dx,dy,dz; int pdamage,dist,blastradius=0x20000, fatalradius=0x9000,impulse,damage, scalefactor; statobj_t* checkstat; objtype* check,*owner; if (ob->ticcount) return; damage = EXPLOSION_DAMAGE; owner = (objtype*)(ob->whatever); if ((ob->temp3) && (ob->obclass == p_firebombobj)) { SpawnFirebomb(ob,damage,0); ob->temp3 = 0; } //================== check surrounding actors ============================// for(check = firstactive; check; check=check->nextactive) { if (check == ob) continue; // if (check == owner) // continue; dx = abs(check->x - ob->x); if (dx > blastradius) continue; dy = abs(ob->y - check->y); if (dy > blastradius) continue; dz = ((abs(ob->z - check->z))<<10); if (dz > blastradius) continue; if (check->flags & FL_HEAD) continue; if (check->flags & FL_DYING) continue; if (!(check->flags & FL_SHOOTABLE)) continue; if ((check->obclass >= roboguardobj) && (check->obclass != b_darkmonkobj)) //(check->obclass <= wallopobj)) continue; if (!CheckLine(ob,check,SIGHT)) continue; if (check->obclass == NMEsaucerobj) { NewState(check,&s_explosion1); check->flags &= ~FL_SHOOTABLE; return; } //magdx = abs(dx); //magdy = abs(dy); dist = Find_3D_Distance(dx,dy,dz); SoftError("\ndist: %x\n",dist); //if (dist < 0x10000) // dist = 0x10000; scalefactor = FixedDiv2(1<<16,FixedMul(dist,dist)); //scalefactor = FixedDiv2(1<<16,dist); if (scalefactor > 0x12000) scalefactor = 0x12000; pdamage = FixedMul(damage,scalefactor); SoftError("\ndamage: %d, scalefactor: %x\n",pdamage,scalefactor); impulse = FixedMul(EXPLOSION_IMPULSE,scalefactor); if (check->obclass == playerobj) { check->target = owner; if (check->flags & FL_AV) pdamage = 0; } if (check->obclass < roboguardobj) { SoftError("\nhitpoints before: %d",check->hitpoints); DamageThing(check,pdamage); SoftError("\nhitpoints after: %d",check->hitpoints); if ((check->hitpoints <= 0) && (gamestate.violence == vl_excessive) && ((ob->obclass == p_firebombobj) || ((dx < fatalradius) && (dy < fatalradius) && (dz < 20))) ) check->flags |= FL_HBM; GetMomenta(check,ob,&momx,&momy,&momz,impulse); //Debug("\nhitmomx = %d, hitmomy = %d",momx,momy); /*if (M_ISACTOR(owner) && (owner->obclass == playerobj) && (check->hitpoints <=0) ) GivePoints(starthitpoints[gamestate.difficulty][check->obclass]*5); */ Collision(check,owner,momx,momy); check->momentumz += (momz<<6); if ((check->obclass == playerobj) && (check->flags & FL_DYING) && M_ISACTOR(owner)) { if (owner->obclass == playerobj) { if (check->z != nominalheight) BATTLE_PlayerKilledPlayer(battle_kill_with_missile_in_air,owner->dirchoosetime,check->dirchoosetime); else BATTLE_PlayerKilledPlayer(battle_kill_with_missile,owner->dirchoosetime,check->dirchoosetime); } else BATTLE_CheckGameStatus(battle_player_killed,check->dirchoosetime); } } else { if (check->obclass != b_darkmonkobj) { SoftError("non-darkmonk actor %d being helped by explosion",check->obclass); } check->hitpoints += pdamage; } } //======================== check surrounding statics ================ for(checkstat = firstactivestat; checkstat; checkstat=checkstat->nextactive) { if ((!(checkstat->flags & FL_SHOOTABLE)) && (checkstat->itemnumber != stat_priestporridge)) continue; if ((checkstat->itemnumber >= stat_lifeitem1) && (checkstat->itemnumber <= stat_lifeitem4)) continue; dx = abs(checkstat->x - ob->x); dy = abs(checkstat->y - ob->y); dz = ((abs(checkstat->z - ob->z))<<10); if ((dx < blastradius) && (dy < blastradius) && (dz < blastradius)) { dist = Find_3D_Distance(dx,dy,dz)+0xc00; if (dist < 0x10000) dist = 0x10000; scalefactor = FixedDiv2(1<<16,FixedMul(dist,dist)); pdamage = FixedMul(damage,scalefactor); if (checkstat->itemnumber != stat_priestporridge) DamageThing(checkstat,pdamage); else if (!(checkstat->flags & FL_ACTIVE)) { checkstat->flags |= FL_ACTIVE; checkstat->count = 1; //checkstat->numanims = 6; SD_PlaySoundRTP(SD_COOKHEALTHSND,ob->x,ob->y); } } } //======================== check surrounding walls ================ { int tilexlow,tilexhigh; int tileylow,tileyhigh; int radius =0x10000; int x,y; tilexlow = (int)((ob->x-radius) >>TILESHIFT); tileylow = (int)((ob->y-radius) >>TILESHIFT); tilexhigh = (int)((ob->x+radius) >>TILESHIFT); tileyhigh = (int)((ob->y+radius) >>TILESHIFT); for (y=tileylow; y<=tileyhigh; y++) { for (x=tilexlow; x<=tilexhigh; x++) { if ((tilemap[x][y]&0x8000) && (tilemap[x][y]&0x4000) && (abs(ob->z - nominalheight) < 32)) { maskedwallobj_t * mw; mw=maskobjlist[tilemap[x][y]&0x3ff]; if (mw->flags&MW_SHOOTABLE) UpdateMaskedWall(tilemap[x][y]&0x3ff); } } } } } void SpawnScreenEye(objtype *ob) { SpawnNewObj(ob->tilex,ob->tiley,&s_eye1,inertobj); new->targettiley = 0; new->targettilex = GameRandomNumber("eye position",0) + 20; SCREENEYE = new; //RemoveFromArea(new); new->flags |= FL_ABP; MakeActive(new); } void SpawnSuperFatalityGibs(objtype *ob,objtype *attacker) { int crazygibs = (GameRandomNumber("crazy gibs",0) % 6) + 4; int i; if ((MISCVARS->supergibflag == true) && ((crazygibs == 9) || (ludicrousgibs == true)) ) { int olddirect = MISCVARS->directgibs; MISCVARS->directgibs = false; if (ludicrousgibs == false) { if (attacker == player) { AddMessage("Ludicrous Gibs!",MSG_GAME); if (!(attacker->flags&FL_DOGMODE)) SD_Play(PlayerSnds[locplayerstate->player]); } } else { MISCVARS->randgibspeed = true; #ifdef MEDGIBS SpawnParticles(ob,GUTS,150); #else SpawnParticles(ob,GUTS,75); #endif MISCVARS->randgibspeed = false; } SpawnParticles(ob,GUTS,40); MISCVARS->directgibs = olddirect; } for (i=gt_head; i<=crazygibs; i++) { if (((ob->obclass == dfiremonkobj) || (ob->obclass == deathmonkobj)) && (i == gt_leg) ) SpawnParticles(ob,gt_arm,1); else SpawnParticles(ob,i,1); } } boolean Vicious_Annihilation(objtype *ob, objtype *attacker) { if ((ob->flags & FL_HBM) && (gamestate.violence >= vl_high)) { ob->shapeoffset = 0; ob->flags &= ~FL_FULLLIGHT; NewState(ob,(ob->obclass == playerobj)?(&s_remoteguts1):(&s_guts1)); SD_PlaySoundRTP(SD_ACTORSQUISHSND,ob->x,ob->y); if (gamestate.violence == vl_excessive) { int numgibs; objtype *prevlast; numgibs = (GameRandomNumber("excessive guts",0) & 7) + 4; //SoftError("\nnumgibs = %d,gamestate.difficulty = %d",numgibs,gamestate.difficulty); prevlast = LASTACTOR; MISCVARS->fulllightgibs = true; SpawnParticles(ob,GUTS,numgibs); MISCVARS->fulllightgibs = false; for(prevlast = prevlast->next; prevlast; prevlast = prevlast->next) prevlast->momentumz += (prevlast->momentumz >> 1); if ((GameRandomNumber("super gib chance",0) < 100) || (ludicrousgibs == true) ) { MISCVARS->directgibs = true; MISCVARS->gibgravity = GRAVITY/2; // MISCVARS->gibgravity = GRAVITY*2; MISCVARS->fulllightgibs = true; SpawnSuperFatalityGibs(ob,attacker); MISCVARS->fulllightgibs = false; MISCVARS->gibgravity = -1; MISCVARS->directgibs = false; } } return true; } if (ob->flags & FL_GODSTRUCK) { ob->shapeoffset = 0; ob->flags |= (FL_FULLLIGHT); ob->flags &= ~FL_COLORED; ob->momentumx = ob->momentumy = ob->momentumz = 0; KillActor(ob); NewState(ob,&s_vaporized1); return true; } if (ob->flags & FL_SKELETON) { KillActor(ob); ob->shapeoffset = 0; ob->flags &= ~FL_COLORED; ob->momentumx = ob->momentumy = ob->momentumz = 0; NewState(ob,&s_skeleton1); SD_PlaySoundRTP(SD_ACTORBURNEDSND,ob->x,ob->y); return true; } return false; } void SetReverseDeathState(objtype * actor) { switch(actor->obclass) { case lowguardobj: NewState(actor, &s_lowgrddie4rev); //actor->state = &s_lowgrddie4rev; break; case highguardobj: NewState(actor, &s_highgrddie5rev); break; case strikeguardobj: NewState(actor, &s_strikedie4rev); //actor->state = &s_strikedie4rev; break; case blitzguardobj: NewState(actor, &s_blitzdie4rev); //actor->state = &s_blitzdie4rev; break; case triadenforcerobj: NewState(actor, &s_enforcerdie4rev); //actor->state = &s_enforcerdie4rev; break; #if (SHAREWARE == 0) case overpatrolobj: NewState(actor, &s_opdie5rev); //actor->state = &s_opdie5rev; break; case deathmonkobj: NewState(actor, &s_dmonkdie4rev); //actor->state = &s_dmonkdie4rev; break; case dfiremonkobj: NewState(actor, &s_firemonkdie4rev); //actor->state = &s_firemonkdie4rev; break; #endif default: Error("SetReverseDeathState was called with something that can't be handled!"); break; } } int DetermineTimeUntilEnemyIsResurrected(classtype obclass) { switch(obclass) { case lowguardobj: return gamestate.TimeCount/VBLCOUNTER + 60; break; case highguardobj: return gamestate.TimeCount/VBLCOUNTER + 90; break; case strikeguardobj: return gamestate.TimeCount/VBLCOUNTER + 65; break; case blitzguardobj: return gamestate.TimeCount/VBLCOUNTER + 60; break; case triadenforcerobj: return gamestate.TimeCount/VBLCOUNTER + 200; break; #if (SHAREWARE == 0) case overpatrolobj: return gamestate.TimeCount/VBLCOUNTER + 75; break; case deathmonkobj: return gamestate.TimeCount/VBLCOUNTER + 150; break; case dfiremonkobj: return gamestate.TimeCount/VBLCOUNTER + 175; break; #endif default: return -1; //TODO: Return -1 for every entry that isn't any of the above break; } } extern Queue * enemiesToRes[8]; void AddEnemyToResurrectList(objtype * ob) { ob->resurrectAtTime = DetermineTimeUntilEnemyIsResurrected(ob->obclass); if (ob->resurrectAtTime == -1) { free(ob); return; } SetReverseDeathState(ob); switch(ob->obclass) { case lowguardobj: Enqueue(enemiesToRes[0], ob); break; case highguardobj: Enqueue(enemiesToRes[1], ob); break; case strikeguardobj: Enqueue(enemiesToRes[2], ob); break; case blitzguardobj: Enqueue(enemiesToRes[3], ob); break; case triadenforcerobj: Enqueue(enemiesToRes[4], ob); break; #if (SHAREWARE == 0) case overpatrolobj: Enqueue(enemiesToRes[5], ob); break; case deathmonkobj: Enqueue(enemiesToRes[6], ob); break; case dfiremonkobj: Enqueue(enemiesToRes[7], ob); break; #endif default: Error("Unknown organic enemy type detected in AddEnemyToResurrectList"); break; } //enqueue(&enemiesToRes, ob); } void FreeUpResurrectList() { int x = 0; for (x = 0; x < 8; x++) { ClearQueue(enemiesToRes[x]); } } void SetAfterResurrectState(objtype * actor, statetype * doWhat) { statetype * state = actor->state; while(state->next != NULL) { state = state->next; } state->next = doWhat; } void SpawnDuringGameWithState (classtype which, int tilex, int tiley, int dir, int ambush, statetype * temp) { //statetype *temp; #if (SHAREWARE == 1) switch(which) { case overpatrolobj: case wallopobj: case deathmonkobj: case dfiremonkobj: case b_darianobj: case b_heinrichobj: case b_darkmonkobj: Error("\n%s actor at %d,%d not allowed in shareware !",debugstr[which],tilex,tiley); break; default: ; } #endif if (which > dfiremonkobj) { return; } if (!CheckTile(tilex,tiley)) FindEmptyTile(&tilex, &tiley); SpawnNewObj(tilex,tiley,temp,which); if (ambush) new->flags |= FL_AMBUSH; StandardEnemyInit(new,dir); if (which == b_darkmonkobj) { new->flags |= (FL_NOFRICTION);//|FL_INVULNERABLE); new->speed = ENEMYRUNSPEED*2; } ConnectAreas(); } void ResurrectEnemies() { objtype * actor; int currTime = gamestate.TimeCount/VBLCOUNTER; int index; for (index = 0; index < 8; index++) { if (enemiesToRes[index]->NumOfItems == 0) { continue; } actor = enemiesToRes[index]->Head->data; if (currTime >= actor->resurrectAtTime) { SD_PlaySoundRTP(SD_PLAYERSPAWNSND, actor->x, actor->y); SpawnDuringGameWithState (actor->obclass,actor->tilex,actor->tiley,actor->dir, 1, actor->state); Dequeue(enemiesToRes[index]); gamestate.killcount--; } } } void SpawnDuringGame (classtype which, int tilex, int tiley, int dir, int ambush) { statetype *temp; #if (SHAREWARE == 1) switch(which) { case overpatrolobj: case wallopobj: case deathmonkobj: case dfiremonkobj: case b_darianobj: case b_heinrichobj: case b_darkmonkobj: Error("\n%s actor at %d,%d not allowed in shareware !",debugstr[which],tilex,tiley); break; default: ; } #endif if (which > dfiremonkobj) { return; } if ((temp = UPDATE_STATES[STAND][which-lowguardobj]) != NULL) { if (!CheckTile(tilex, tiley)) FindEmptyTile(&tilex, &tiley); SpawnNewObj(tilex,tiley,temp,which); if (ambush) new->flags |= FL_AMBUSH; StandardEnemyInit(new,dir); if (which == b_darkmonkobj) { new->flags |= (FL_NOFRICTION);//|FL_INVULNERABLE); new->speed = ENEMYRUNSPEED*2; } } ConnectAreas(); } /* ======================== = = BeginEnemyFatality = ======================== */ extern boolean enableZomROTT; void BeginEnemyFatality(objtype *ob,objtype *attacker) { if ((attacker == player) && (ob->obclass < (NUMENEMIES + 2))) { GivePoints(starthitpoints[gamestate.difficulty][ob->obclass]*5); if (timelimitenabled) timelimit+=VBLCOUNTER; } ob->flags |= FL_DYING; ob->soundhandle = -1; if (Vicious_Annihilation(ob,attacker)) return; else if (enableZomROTT) { objtype * copyOfObject = malloc(sizeof(objtype)); memcpy(copyOfObject, ob, sizeof(objtype)); AddEnemyToResurrectList(copyOfObject); } if ((ob->obclass == patrolgunobj) && (ob->temp1 == -1)) NewState(ob,&s_robogrddie1); else if (ob->obclass == collectorobj) { if ((!M_ISACTOR(attacker)) || (attacker->obclass != playerobj)) RespawnEluder(); else BATTLE_CheckGameStatus(battle_shot_deluder,attacker->dirchoosetime); NewState(ob,&s_explosion1); MISCVARS->gibgravity = GRAVITY/2; MISCVARS->fulllightgibs = true; SpawnParticles(ob,gt_sparks,100); MISCVARS->fulllightgibs = false; MISCVARS->gibgravity = -1; } else { statetype *temp; if ((ob->obclass == blitzguardobj) && (ob->state->condition & SF_FAKING) ) { NewState(ob,&s_blitzstruggledie1); ob->flags &= ~FL_FULLLIGHT; } else if ((ob->obclass == blitzguardobj) && (ob->state->condition & SF_DOWN) ) { NewState(ob,&s_blitzplead7); ob->flags &= ~FL_FULLLIGHT; } else if ((temp= M_S(DIE)) != NULL) { if (LOW_VIOLENCE_DEATH_SHOULD_BE_SET(ob)) SET_DEATH_SHAPEOFFSET(ob); NewState(ob,temp); ob->flags &= ~FL_FULLLIGHT; } else Error("Null dead state called in Collision, obclass %d",ob->obclass); } } /* ======================== = = BeginPlayerFatality = ======================== */ void BeginPlayerFatality(objtype *ob,objtype *attacker) { playertype *pstate; M_LINKSTATE(ob,pstate); ob->flags &= ~(FL_ELASTO|FL_GODMODE|FL_DOGMODE|FL_NOFRICTION|FL_RIDING); ob->flags |= FL_DYING; pstate->weapon = -1; if (BATTLEMODE) SD_PlaySoundRTP (SD_PLAYERTCDEATHSND+(pstate->player),ob->x,ob->y); if (Vicious_Annihilation(ob,attacker) == false) { if (LOW_VIOLENCE_DEATH_SHOULD_BE_SET(ob)) SET_DEATH_SHAPEOFFSET(ob); NewState(ob,&s_remotedie1); ob->flags &= ~FL_FULLLIGHT; } } /* ======================== = = BeginEnemyHurt = ======================== */ void BeginEnemyHurt(objtype *ob) { statetype *temp; if ((temp= M_S(COLLIDE1)) != NULL) { if ((ob->obclass == blitzguardobj) && (ob->state->condition & SF_FAKING) ) { ob->temp1 = 1; ob->dirchoosetime = 0; T_PlayDead(ob); } else { if ((ob->obclass == triadenforcerobj) && (GameRandomNumber("george pain chance",0) < (50 + (gamestate.difficulty<<6)) ) ) { ob->flags &= ~FL_FULLLIGHT; return; } if (LOW_VIOLENCE_PAIN_SHOULD_BE_SET(ob)) SET_PAIN_SHAPEOFFSET(ob); if (GameRandomNumber("Collision",0) < 128) NewState(ob,temp); else NewState(ob,M_S(COLLIDE2)); } ob->flags &= ~FL_FULLLIGHT; ob->ticcount = PAINTIME; if (ob->obclass == strikeguardobj) ob->ticcount >>= 1; } } void Collision(objtype*ob,objtype *attacker,int hitmomentumx,int hitmomentumy) { int ocl; if ((!(ob->flags & FL_SHOOTABLE)) || (ob->flags & FL_DYING)) return; ocl = ob->obclass; ob->momentumx += hitmomentumx; ob->momentumy += hitmomentumy; if ((ocl == playerobj) && (gamestate.battlemode == battle_Eluder)) return; //insertion 5 if (ocl != playerobj) { if ((!(ob->flags & FL_ATTACKMODE)) && (TABLE_ACTOR(ob))) ActivateEnemy(ob); if (ob->hitpoints <= 0) BeginEnemyFatality(ob,attacker); else if (ocl != roboguardobj)// && (ob->state->think != T_Collide)) BeginEnemyHurt(ob); } else { playertype *pstate; if ((ob->flags & FL_GODMODE) || (ob->flags & FL_DOGMODE) || godmode) return; M_LINKSTATE(ob,pstate); if (pstate->health<=0) BeginPlayerFatality(ob,attacker); else ob->flags |= FL_PAIN; } } void T_BossExplosions(objtype*ob) { if (ob->temp1) { if (ob->dirchoosetime) ob->dirchoosetime --; else { int randtime,randangle,randdist,sound; statetype *nstate; ob->temp1 --; randtime = GameRandomNumber("Boss Explosion Time",0); ob->dirchoosetime = 10; if (randtime < 128) ob->dirchoosetime >>= 1; randangle = (GameRandomNumber("Boss Explosion Angle",0) << 3); randdist = (GameRandomNumber("Boss Explosion Distance",0) << 7)+0x4000; sound = SD_EXPLODEFLOORSND; if (randtime < 128) { nstate = &s_explosion1; sound++; } #if (SHAREWARE == 0) else nstate = &s_grexplosion1; #endif SpawnMissile(ob,inertobj,0,randangle,nstate,randdist); SD_PlaySoundRTP(sound,new->x,new->y); } } } gib_t RandomGutsType(void) { int rand = GameRandomNumber("gut random",0); if (rand < 128) return gt_organ; //if (rand < 160) return gt_rib; //return gt_pinkorgan; } void SpawnParticles(objtype*ob,int which,int numparticles) { int randphi,randtheta,i,nspeed; boolean eyespawned = false; int gibtype; int randadj; if ((ob->z <= -64) && (sky == 0)) //shouldn't happen return; if (((which == GUTS) || (which == RANDOM)) && (gamestate.violence < vl_high)) which = gt_sparks; gibtype = which; for(i=0; i<numparticles; i++) { int ordertemp; /* DDOI - Watcom evaluates the mult order diff */ randphi = (GameRandomNumber("particle generate phi",0) << 3); // randadj = RandomSign() * (GameRandomNumber("rand gib adjust",0) >> 4); ordertemp = (GameRandomNumber("rand gib adjust",0) >> 4); randadj = RandomSign() * ordertemp; if (ob->z > (nominalheight - 32)) randphi &= ((ANGLES/2) - 1); randtheta = (GameRandomNumber("particle generate theta",0) << 3); nspeed = MISCVARS->gibspeed; if (which == RANDOM) { if (GameRandomNumber("random gib",0) < 128) gibtype = RandomGutsType(); else gibtype = gt_sparks; } if ((which == GUTS) || (which == DISEMBOWEL)) { gibtype = RandomGutsType(); if (which == DISEMBOWEL) { randphi>>=2; randtheta=ob->temp1+(randtheta>>3)-(randtheta>>4); } } if (lowmemory && (gibtype >= gt_rib) && (gibtype <= gt_limb)) gibtype = gt_organ; if ( // (gibtype >= gt_organ) && (gibtype <= gt_limb) && (MISCVARS->numgibs >= MAXGIBS) ) return; if (gibtype == gt_lsoul) { SpawnNewObj(ob->tilex,ob->tiley,&s_littlesoul,inertobj); randphi = 0; } #if (SHAREWARE == 0) else if (gibtype == gt_spit) SpawnNewObj(ob->tilex,ob->tiley,&s_slop1,inertobj); #endif else { SpawnNewObj(ob->tilex,ob->tiley,&s_gibs1,inertobj); new->shapeoffset = gibtype*12; NewState(new,new->state); } if (MISCVARS->directgibs == true) { int dx,dy,dz; dx = PLAYER[0]->x - ob->x; dy = ob->y - PLAYER[0]->y; randtheta = AngleBetween(ob,PLAYER[0]) + (randadj<<4); dz = 100 + (randadj<<3); #ifdef MEDGIBS nspeed = 0x2800; #else nspeed = 0x2800 + (randadj<<7); #endif randphi = atan2_appx(FindDistance(dx,dy),dz<<10); } if ((eyespawned == false) && (which == GUTS) && (ob->obclass != playerobj) ) { eyespawned = true; new->flags |= FL_EYEBALL; } if ((gibtype >= gt_organ) && (gibtype <= gt_limb)) { new->dirchoosetime = GIBVALUE; MISCVARS->numgibs ++; } new->temp2 = gibtype; new->temp3 = (MISCVARS->gibgravity == -1)?(GRAVITY):(MISCVARS->gibgravity); new->speed = nspeed>>1; #ifndef MEDGIBS if (MISCVARS->randgibspeed == true) new->speed += (randadj << 11); #endif // if (ob->state == &s_snakefireworks) new->z = ob->z; Fix(randphi); Fix(randtheta); Set_3D_Momenta(new,new->speed,randtheta,randphi); new->momentumz <<= 6; new->flags |= (FL_NOFRICTION|FL_CRAZY|FL_ABP|FL_NEVERMARK); if (MISCVARS->fulllightgibs == true) new->flags |= FL_FULLLIGHT; new->dir = west; new->whatever = ob; MakeActive(new); } } void T_SlideDownScreen(objtype *ob) { ob->targettiley += 12; if (ob->targettiley > 300) { NewState(ob,&s_megaremove); SCREENEYE = NULL; } } void T_SpawnSoul(objtype*ob) { if (ob->ticcount) return; SpawnNewObj(ob->tilex,ob->tiley,&s_bigsoul,inertobj); new->momentumz = -4000; new->flags |= (FL_NOFRICTION|FL_CRAZY|FL_ABP|FL_NEVERMARK); new->z = ob->z; new->dir = west; MakeActive(new); SpawnParticles(ob,gt_lsoul,6); } void BloodDrip(objtype *ob,int tilex,int tiley) { int dx,dy,x,y,scale; dx = ob->tilex - tilex; dy = ob->tiley - tiley; if (!dy) { scale = (ob->momentumx)?(FixedDiv2(ob->momentumy,ob->momentumx)):(0); x = (dx < 0)?(tilex << 16):((tilex+1) << 16); y = FixedMul(x - ob->x,scale) + ob->y; } else if (!dx) { scale = (ob->momentumy)?(FixedDiv2(ob->momentumx,ob->momentumy)):(0); y = (dy < 0)?(tiley << 16):((tiley+1) << 16); x = FixedMul(y - ob->y,scale) + ob->x; } ob->temp2 = (GameRandomNumber("BloodDrip",0) << 9) + 0xc000; ob->temp1 = (ob->z<<16); SetFinePosition(ob,x,y); ob->shapeoffset = 0; NewState(ob,&s_blooddrip1); } void T_BloodFall(objtype*ob) { ob->temp1 += ob->temp2; ob->z = (ob->temp1 >> 16); if (ob->z >= maxheight) { ob->shapeoffset = 12; MISCVARS->numgibs--; NewState(ob,&s_gibsdone1); ob->z = nominalheight; } } void T_Xylophone(objtype*ob) { if (!ob->ticcount) SD_PlaySoundRTP(SD_ACTORSKELETONSND,ob->x,ob->y); } void T_ParticleGenerate(objtype*ob) { if (ob->dirchoosetime) ob->dirchoosetime--; else { SetGibSpeed(0x3000); SpawnParticles(ob,gt_sparks,(GameRandomNumber("particle count",0) % 10) + 7); ResetGibSpeed(); ob->dirchoosetime = 10; if (GameRandomNumber("particle generator choose time",0) < 128) ob->dirchoosetime >>= 1; } } void T_Particle(objtype*ob) { int dx,dy,dz; ob->z += (ob->momentumz>>16); if ((ob->z >= nominalheight) || (!ob->momentumz)) { if (ob->z >= nominalheight) ob->z = nominalheight; //done: if (ob->temp2 == gt_spit) ob->state = NULL; else { if (ob->dirchoosetime == GIBVALUE) { MISCVARS->numgibs--; SD_PlaySoundRTP(GIBSOUND,ob->x,ob->y); } NewState(ob,&s_gibsdone1); } return; } else if ((ob->z < -64) && (sky == 0)) { ob->momentumz = 1; //any positive value will do ob->z = -64; } ob->momentumz += ob->temp3; ActorMovement(ob); if (!BATTLEMODE) { dx = abs(ob->x - PLAYER[0]->x); dy = abs(ob->y - PLAYER[0]->y); dz = abs(ob->z - PLAYER[0]->z); #if (SHAREWARE==0) if ((ob->flags & FL_EYEBALL) && (dx < 0x20000) && (dy < 0x20000) && (dz < 64) && (GameRandomNumber("eye chance",0) < 15) && (SCREENEYE == NULL) && (locplayerstate->weapon != wp_dog) ) #else if ((ob->flags & FL_EYEBALL) && (dx < 0x20000) && (dy < 0x20000) && (dz < 64) && (GameRandomNumber("eye chance",0) < 15) && (SCREENEYE == NULL) ) #endif SpawnScreenEye(ob); } //MoveActor(ob); //if ((!ob->momentumx) && (!ob->momentumy)) //goto done; } void DropItemInEmptyTile(int item,int tilex,int tiley) { int stilex = tilex; int stiley = tiley; FindEmptyTile(&stilex,&stiley); SpawnStatic(stilex,stiley,item,9); LASTSTAT->flags |= FL_ABP; MakeStatActive(LASTSTAT); } extern boolean enableExtraPistolDrops; void KillActor(objtype*ob) { int ocl; ocl = ob->obclass; //GivePoints(starthitpoints[gamestate.difficulty][ob->obclass]*5); if ((ocl == highguardobj) && (GameRandomNumber("Drop mp40 chance",0) < 25)) { DropItemInEmptyTile(stat_mp40,ob->tilex,ob->tiley); } else if ((ocl == blitzguardobj) && (ob->temp3)) { DropItemInEmptyTile(ob->temp3,ob->tilex,ob->tiley); LASTSTAT->ammo = ob->temp2; } else if ((ocl >= lowguardobj && ocl != highguardobj && ocl <= blitzguardobj) && (GameRandomNumber("Drop extra pistol chance", 0) < 25) && enableExtraPistolDrops) { DropItemInEmptyTile(stat_twopistol, ob->tilex, ob->tiley); } if (actorat[ob->tilex][ob->tiley] == (void*)ob) actorat[ob->tilex][ob->tiley] = NULL; gamestate.killcount++; ob->flags &= ~FL_SHOOTABLE; ob->flags &= ~FL_BLOCK; ob->flags |= FL_NEVERMARK; #if (SHAREWARE == 0) if (ocl == b_darksnakeobj) { if (ob == SNAKEHEAD) { SpawnNewObj(ob->tilex,ob->tiley,&s_megaexplosions,inertobj); new->temp1 = 7000; new->flags |= FL_ABP; EXPLOSIONS = new; NewState(ob,&s_darkmonkheaddie1); MakeActive(new); ob->dirchoosetime = 0; } else { objtype *temp; SNAKEEND = (objtype*)(ob->target); SNAKEEND->whatever = NULL; NewState(ob,&s_explosion1); for(temp=SNAKEHEAD; temp; temp = (objtype*)(temp->whatever)) temp->speed += 0x500; } } else #endif { ob->whatever = NULL; if (ob->obclass!=playerobj) ob->target = NULL; } if ((ob->flags & FL_KEYACTOR) && (ocl!=playerobj) && (ocl != blitzguardobj)) { MISCVARS->KEYACTORSLEFT --; if (!MISCVARS->KEYACTORSLEFT) { SpawnNewObj(ob->tilex,ob->tiley,&s_timekeeper,inertobj); new->flags |= FL_ABP; MakeActive(new); } } } void T_End(objtype *ob) { if (ob->ticcount) return; if (MAPSPOT(0,5,2) == LASTLEVELVALUE) playstate = ex_gameover; else playstate = ex_bossdied; } void T_Convert(objtype*ob) { if (ob->ticcount) return; if (ob->obclass == playerobj) { if (ob->state == &s_vaporized8) { T_SpawnSoul(ob); NewState(ob,&s_voidwait); } else if (ob->state == &s_skeleton48) { playertype *pstate; M_LINKSTATE(ob,pstate); if ((pstate->falling == true) || (!ob->momentumz) ) NewState(ob,&s_ashwait); else CheckPlayerSpecials(ob); } } else { if (ob->state == &s_vaporized8) T_SpawnSoul(ob); else if (ob->state == &s_skeleton48) TurnActorIntoSprite(ob); } } void TurnActorIntoSprite(objtype *ob) { statobj_t*temp; objtype *tactor; if (!firstemptystat) temp = (statobj_t*)Z_LevelMalloc(sizeof(statobj_t),PU_LEVELSTRUCT,NULL); else { temp = lastemptystat; //SoftError("\nfree actor available"); RemoveFromFreeStaticList(lastemptystat); } if (temp) { if ((ob->obclass == blitzguardobj) && ((ob->flags & FL_PLEADING) || (ob->flags & FL_UNDEAD)) ) MISCVARS->NUMBEGGINGKEVINS = 0; if (ob->obclass == roboguardobj) { for(tactor=firstareaactor[ob->areanumber]; tactor; tactor=tactor->nextinarea) { if (tactor == ob) continue; if (tactor->obclass != ob->obclass) continue; if (tactor->flags & FL_DYING) continue; if (!tactor->state->think) NewState(tactor,UPDATE_STATES[PATH][tactor->obclass-lowguardobj]); } } memset(temp,0,sizeof(*temp)); temp->shapenum = ob->shapenum; temp->linked_to = -1; temp->whichstat = statcount ++; SetFinePosition(temp,ob->x,ob->y); temp->areanumber = MAPSPOT(temp->tilex,temp->tiley,0)-AREATILE; // if ((temp->areanumbers<=0) || (temp->areanumber>NUMAREAS)) // Error ("Sprite at x=%ld y=%ld type=%ld has an illegal areanumber\n",tilex,tiley,mtype); temp->visspot = &spotvis[temp->tilex][temp->tiley]; temp->which = SPRITE; temp->itemnumber = -1; temp->flags = FL_DEADBODY; if (ob->flags & FL_COLORED) { playertype *pstate; M_LINKSTATE(ob,pstate); temp->flags |= FL_COLORED; temp->hitpoints = pstate->uniformcolor; } temp->z = ob->z; AddStatic(temp); // sprites[temp->tilex][temp->tiley] = temp; if (areabyplayer[temp->areanumber]) { temp->flags |= FL_ABP; MakeStatActive(temp); } if (ob->state != &s_guts12) actorat[ob->tilex][ob->tiley] = temp; ob->state = NULL; // say goodbye actor } else Error("Z_LevelMalloc failed in TurnActorIntoSprite!"); } void T_Blood(objtype*ob) { if (ob->dirchoosetime) { ob->dirchoosetime --; return; } ob->dirchoosetime = 35 + (GameRandomNumber("blood time",0) % 20); NewState(ob,&s_deadblood1); } void ActorDeath(objtype*ob) { #if (SHAREWARE == 0) if (ob->obclass == b_heinrichobj) { KillActor(ob); ob->temp1 = ob->dirchoosetime = 5;//10; // init. spin counter for heinrich ob->temp3 = 7; //number of times to stay at fast spin ob->temp2 = ob->dir; //temp2 holds orig. dir. } else if (ob->obclass == b_robobossobj) { objtype *wheels,*head; head = (objtype*)(ob->whatever); wheels = (objtype*)(ob->target); head->flags &= ~(FL_HEAD|FL_SHOOTABLE|FL_BLOCK); head->temp2 = 5; head->flags |= (FL_NOFRICTION|FL_CRAZY); // head->obclass = inertobj; //RemoveObj(wheels); // remove wheels KillActor(ob); ob->whatever = head; ob->target = wheels; //ob->temp1 = 25; //ob->shapeoffset = 0; SpawnNewObj(ob->tilex,ob->tiley,&s_megaexplosions,inertobj); new->temp1 = 18; new->flags |= FL_ABP; MakeActive(new); //ob->state = NULL; NewState(ob,&s_NMEdeathbuildup); } else #endif if ((ob->state == ob->state->next) && (ob->flags & FL_DYING) ) { KillActor(ob); TurnActorIntoSprite(ob); if (LASTSTAT->z < nominalheight) { if ((!IsPlatform(LASTSTAT->tilex,LASTSTAT->tiley)) && (DiskAt(LASTSTAT->tilex,LASTSTAT->tiley) == NULL) ) { SpawnParticles(ob,GUTS,10 + gamestate.difficulty); RemoveStatic(LASTSTAT); } } /* else if ((GameRandomNumber("blood spray",0) < 300) && areabyplayer[ob->areanumber]) {ob->shapeoffset = 0; ob->temp2 = ob->temp3 = 0; ob->temp1 = 10; NewState(ob,&s_deadblood1); return; } */ } } void BeginPostPainAction(objtype *ob) { if ((ob->obclass == strikeguardobj) && (ob->target == (void*)PLAYER[0]) ) { //ob->target = NULL; if (LOW_VIOLENCE_DEATH_IS_SET(ob)) RESET_DEATH_SHAPEOFFSET(ob); if (GameRandomNumber("T_Collide",0) < 128) NewState(ob,&s_strikerollright1); else NewState(ob,&s_strikerollleft1); SelectRollDir(ob); if (ob->dirchoosetime) { SD_PlaySoundRTP(SD_STRIKEROLLSND,ob->x,ob->y); return; } } if (LOW_VIOLENCE_PAIN_IS_SET(ob)) RESET_PAIN_SHAPEOFFSET(ob); if (ob->obclass < roboguardobj) ob->flags &= ~FL_NOFRICTION; if ( (ob->obclass == blitzguardobj) && (gamestate.violence == vl_excessive) && (GameRandomNumber("blitzplead",0) < 128) && (MISCVARS->NUMBEGGINGKEVINS == 0) && (ob->flags & FL_TARGET) && (ob->hitpoints < (starthitpoints[gamestate.difficulty][ob->obclass] >> 1)) && (ob->momentumz == 0) && (!(ob->flags & FL_UNDEAD)) ) { NewState(ob,&s_blitzplead1); MISCVARS->NUMBEGGINGKEVINS = 1; ob->momentumx = ob->momentumy = 0; ob->flags |= FL_PLEADING; ob->flags &= ~FL_TARGET; ob->dirchoosetime = 165; ob->hitpoints = 1; } else { NewState(ob,M_S(CHASE)); ob->targettilex = ob->targettiley = 0; ob->dirchoosetime = 0; } } void T_Collide(objtype*ob) { if (!(ob->flags & FL_SHOOTABLE)) return; ActorMovement(ob); if (ob->state == NULL) return; if (ob->ticcount) return; if (ob->hitpoints <= 0) { if ((ob->soundhandle == -1) && (!ob->ticcount) && (ob->state->next->tictime == 0) ) { ob->soundhandle = SD_PlaySoundRTP(ACTORTHUDSND,ob->x,ob->y); } if (ob->momentumx || ob->momentumy || ob->momentumz) return; ActorDeath(ob); return; } BeginPostPainAction(ob); } /* ========================================================================= = = Special Blitzguard Functions = ========================================================================= */ /* ================= = = T_Plead = ================= */ void T_Plead(objtype*ob) { int handle; ActorMovement(ob); if (ob->dirchoosetime) { if (!(ob->dirchoosetime & 31)) { int random = GameRandomNumber("blitz plead sound",0); if (random < 80) SD_PlaySoundRTP(SD_BLITZPLEADSND,ob->x,ob->y); else if (random < 160) SD_PlaySoundRTP(SD_BLITZPLEAD1SND,ob->x,ob->y); else SD_PlaySoundRTP(SD_BLITZPLEAD2SND,ob->x,ob->y); } ob->dirchoosetime --; return; } ob->hitpoints = (starthitpoints[gamestate.difficulty][blitzguardobj]>>1); //ob->flags |= FL_DYING; ob->flags |= FL_UNDEAD; SET_DEATH_SHAPEOFFSET(ob); NewState(ob,&s_blitzfakedie1); ob->flags &= ~FL_PLEADING; ob->dirchoosetime = (GameRandomNumber("blitz fake time",0) >> 2) + 70; handle = SD_PlaySoundRTP(SD_BLITZOUCHSND,ob->x,ob->y); SD_SetSoundPitch (handle,-500); ob->temp1 = 0; ob->temp1 = (GameRandomNumber("blitz visible rise",0) < 60); } /* ================= = = T_ReallyDead = ================= */ void T_ReallyDead(objtype *ob) { ActorMovement(ob); if ((!ob->ticcount) && (LOW_VIOLENCE_DEATH_SHOULD_BE_SET(ob))) SET_DEATH_SHAPEOFFSET(ob); } /* ================= = = T_PlayDead = ================= */ void T_PlayDead(objtype *ob) { int dangle; ob->flags &= ~FL_DYING; ActorMovement(ob); if (ob->dirchoosetime) { ob->dirchoosetime--; return; } dangle = abs(player->angle - AngleBetween(player,ob)); if (dangle > ANGLES/2) dangle = ANGLES - dangle; if (ob->temp1 || (dangle > ANGLES/4)) { if (LOW_VIOLENCE_DEATH_IS_SET(ob)) RESET_DEATH_SHAPEOFFSET(ob); ob->temp1 = 0; NewState(ob,&s_blitzrise2); } } void AdjustAngle(int maxadjust, short int *currangle,int targetangle) { int dangle,i,magangle; for(i=0; i<maxadjust; i++) { dangle = *currangle - targetangle; if (dangle) { magangle = abs(dangle); if (magangle > (ANGLES/2)) { if (dangle > 0) (*currangle) ++; else (*currangle) --; } else { if (dangle > 0) (*currangle) --; else (*currangle) ++; } Fix(*currangle); } } } void ResolveMinimumDistance(objtype *heatseeker, objtype *potential_target, int *currmin) { int currdist,angle,magangle; currdist = FindDistance((heatseeker->x-potential_target->x), (heatseeker->y-potential_target->y)); angle = AngleBetween(heatseeker,potential_target); if (heatseeker->obclass != p_godballobj) { magangle = abs(heatseeker->angle - angle); if (magangle > VANG180) magangle = ANGLES - magangle; if (magangle > ANGLESDIV8) return; } if (currdist > LOOKAHEAD) return; if (currdist < (*currmin)) { *currmin = currdist; heatseeker->target = potential_target; } } void HeatSeek(objtype*ob) { int xydist; int mindist; objtype* tactor; objtype* owner; statobj_t* tstat; int angle,dz,yzangle,adjust; int dx,dy; owner=(objtype *)ob->whatever; if (ob->dirchoosetime) ob->dirchoosetime --; else { if (!ob->target) { mindist = 0x7fffffff; for (tactor=firstactive; tactor; tactor=tactor->nextactive) { if (tactor == owner) continue; if (tactor->flags & FL_HEAD) continue; if ((tactor == ob) || (!(tactor->flags & FL_SHOOTABLE)) || (tactor->flags & FL_DYING)) continue; if (!CheckLine(ob,tactor,SIGHT)) continue; if ((tactor->obclass == bladeobj) || (tactor->obclass == NMEsaucerobj)) continue; ResolveMinimumDistance(ob,tactor,&mindist); } if (ob->obclass != p_godballobj) { for(tstat=firstactivestat; tstat; tstat=tstat->nextactive) { if (!(tstat->flags & FL_HEAT)) continue; if (!CheckLine(ob,tstat,SHOOT)) continue; ResolveMinimumDistance(ob,(objtype*)tstat,&mindist); } } if (!ob->target) ob->dirchoosetime = 5; } else //if (ob->target != owner) { tactor = (objtype*)ob->target; if ((!tactor->nextactive) && (!tactor->prevactive)) { ob->target = NULL; return; } dx = tactor->x - ob->x; dy = ob->y - tactor->y; dz = ob->z - tactor->z; //xydist = FixedSqrtHP((FixedMul(dx,dx) + FixedMul(dy,dy))>>16); xydist = FindDistance(dx,dy); angle = atan2_appx(dx,dy); adjust = (ob->obclass == p_godballobj)?(GODHAPT):(HAAPT); AdjustAngle(adjust,&(ob->angle),angle); ob->dir = angletodir[ob->angle]; ob->momentumx = FixedMul(ob->speed,costable[ob->angle]); ob->momentumy = -FixedMul(ob->speed,sintable[ob->angle]); yzangle = atan2_appx(xydist,(dz<<10)); adjust = (ob->obclass == p_godballobj)?(GODVAPT):(VAAPT); AdjustAngle(adjust,&(ob->yzangle),yzangle); ob->momentumz = -(FixedMul(ob->speed,sintable[ob->yzangle])); } } } void Stagger(objtype*ob) { int randadj; randadj = (int)(GameRandomNumber("Stagger",1) >> 3); if (!ob->dirchoosetime) { ob->momentumz = ob->temp1 + (RandomSign() << 12); ob->dirchoosetime = 6; } else ob->dirchoosetime --; if ((ob->z + (ob->momentumz >> 10)) > (maxheight-12)) ob->momentumz = -ob->momentumz; else if ((ob->z < 5) && (!sky)) ob->z = 5; ob->angle += (RandomSign()*randadj); Fix(ob->angle); ob->momentumx = FixedMul(ob->speed,costable[ob->angle]); ob->momentumy = -FixedMul(ob->speed,sintable[ob->angle]); ob->dir = angletodir[ob->angle]; } void SpawnSplit(objtype *ob,int angle) { Fix(angle); SpawnMissile(ob,p_heatseekobj,ob->speed,angle,&s_p_bazooka1,0x4000); new->momentumz = ob->momentumz; new->whatever = ob->whatever; } void SplitMissile(objtype*ob) { SD_PlaySoundRTP(SD_SPLITSND,ob->x,ob->y); if (ob->soundhandle != -1) SD_StopSound(ob->soundhandle); SpawnSplit(ob,ob->angle + ANGLES/12); SpawnSplit(ob,ob->angle - ANGLES/12); if (missobj == ob) { if (GameRandomNumber("split misscam",0)<128) missobj = LASTACTOR; else missobj = LASTACTOR->prev; } ob->state=NULL; // get rid of current missile } void SpawnMissileSmoke(objtype *ob) { if (!ValidAreanumber(AREANUMBER(ob->tilex,ob->tiley))) return; SpawnStatic(ob->tilex,ob->tiley,stat_missmoke,-1); LASTSTAT->flags |= FL_ABP; MakeStatActive(LASTSTAT); SetFinePosition(LASTSTAT,ob->x,ob->y); LASTSTAT->z = ob->z+3; } void T_Projectile (objtype *ob) { objtype *owner; playertype * pstate; owner = (objtype*)(ob->whatever); if (owner->obclass == playerobj) M_LINKSTATE(owner,pstate); if ((ob->soundhandle != -1) && (!(oldpolltime & 7)) ) SD_PanRTP(ob->soundhandle,ob->x,ob->y); #if (SHAREWARE == 0) if (ob->obclass == h_mineobj) { if (!ob->dirchoosetime) { NewState(ob,&s_grexplosion1); SD_PlaySoundRTP(SD_KRISTMINEHITSND,ob->x,ob->y); } else ob->dirchoosetime --; if ( (ob->state == &s_mine1) && (!ob->ticcount) ) SD_PlaySoundRTP(BAS[ob->obclass].operate,ob->x,ob->y); } #endif if (!ob->ticcount) { if (ob->state == &s_p_grenade) ob->momentumz += (GRAVITY>>6); else if (ob->state == &s_grenade_fall6) { NewState(ob,&s_explosion1); return; } } if (ob->obclass == p_drunkmissileobj) { if (ob->temp3 > 0) { ob->temp3 --; Stagger(ob); } else { if (ob->target == NULL) Stagger(ob); HeatSeek(ob); } } else if (ob->temp1 == NME_DRUNKTYPE) Stagger(ob); else if ((ob->obclass == p_heatseekobj) || (ob->obclass == dm_heatseekobj) || (ob->temp1 == NME_HEATSEEKINGTYPE) || (ob->obclass == p_godballobj) ) HeatSeek(ob); else if ((ob->obclass == p_splitmissileobj) && (!pstate->buttonstate[bt_attack]) ) { SplitMissile(ob); return; } if ((!BATTLEMODE) && (!(ob->ticcount & 7)) && (ob->obclass != p_firewallobj) && (ob->obclass != p_kesobj) && (ob->obclass != p_godballobj) && ((ob->obclass >= p_bazookaobj) || (ob->obclass == missileobj)) )// && SpawnMissileSmoke(ob); MissileMovement(ob); if (ob->obclass == grenadeobj) { if (ob->temp1 > 0) ob->temp1 -= ob->speed; else if (!(ob->flags & FL_DONE)) { NewState(ob,&s_grenade_fall1); ob->flags |= FL_DONE; } } } void StartFirewall(objtype*ob, int newz) { objtype *owner = (objtype*)(ob->whatever); MISCVARS->firespawned = 0; owner->temp1 = 0; SpawnFirewall(ob,2,newz); if (missobj == ob) missobj = LASTACTOR; NewState(ob,&s_megaremove); } void MissileMovement(objtype*ob) { int tryx, tryy,tryz; tryx = ob->x + ob->momentumx; tryy = ob->y + ob->momentumy; tryz = ob->z + (ob->momentumz >> 10); if (!MissileTryMove (ob, tryx, tryy, tryz)) return; ob->z += (ob->momentumz >> 10); MoveActor(ob); } #define DetonateMissile(x,y) \ {MissileHit(x,y); \ return false; \ } \ #define QuietDetonate(ob) \ { \ if (ob->soundhandle != -1) \ SD_StopSound(ob->soundhandle); \ if (ob == missobj) \ missobj = NULL; \ NewState(ob,&s_megaremove); \ return false; \ } extern boolean ricochetingRocketsEnabled; boolean MissileTryMove(objtype*ob,int tryx,int tryy,int tryz) { int tilexlow,tileylow,tilexhigh,tileyhigh,x,y, trytilex,trytiley,dx,dy,dzt,dztp1,radius, sprrad,actrad,tcl,ocl,oldsrad,area,zdist, wall; objtype *temp; wall_t *tempwall; doorobj_t *tempdoor; int doorn; statobj_t *tempstat; boolean areatried[NUMAREAS] = {0}; sprrad = 0x4500; actrad = ACTORSIZE+0x2800; ocl = ob->obclass; radius = PROJSIZE-0x2200; if (ocl==wallfireobj) radius-=0x3000; trytilex = (tryx >> TILESHIFT); trytiley = (tryy >> TILESHIFT); if (IsWindow(trytilex,trytiley) || (!InMapBounds((tryx>>16),(tryy>>16)))) { QuietDetonate(ob); } /* */ //==== ceiling/floor clipping =================// if (tryz < -30) { if ((sky==0) || (ocl == inertobj)) { DetonateMissile(ob,NULL); } /* else return true; */ /* else { NewState(ob,&s_megaremove); if (missobj == ob) missobj = NULL; return false; } */ } if (tryz > (maxheight-10)) { if ((ocl == p_firewallobj) && (!(ob->flags & FL_ISFIRE))) StartFirewall(ob,nominalheight); else MissileHit(ob,NULL); return false; } //=============================================// sprrad = PROJSIZE+0x1000; tilexlow = (int)((tryx-radius) >>TILESHIFT); tileylow = (int)((tryy-radius) >>TILESHIFT); tilexhigh = (int)((tryx+radius) >>TILESHIFT); tileyhigh = (int)((tryy+radius) >>TILESHIFT); oldsrad = sprrad; if (ocl == inertobj) goto walls; area = ob->areanumber; areatried[area] = true; actors: for(temp=firstareaactor[area]; temp; temp=temp->nextinarea) { if (temp == ob) continue; dx = abs(tryx - temp->x); dy = abs(tryy - temp->y); if ((dx > actrad) || (dy > actrad)) continue; if (temp->flags & FL_HEAD) continue; if ((!(temp->flags & FL_BLOCK)) || (temp->flags & FL_DYING)) continue; tcl = temp->obclass; zdist = 32; dzt = abs(tryz - temp->z); if ((tcl == playerobj) && (temp->flags & FL_DOGMODE)) { dzt = abs(tryz - (temp->z + 30)); zdist = 10; } else if (tcl == diskobj) { zdist = 50; } if (dzt > zdist) continue; //if ((ocl==wallfireobj) && (tcl==playerobj) && (temp->flags&FL_DOGMODE) && (dz>15)) // continue; //if ((ocl==playerobj) && // (ob->whatever == (void*)temp)) //continue; if (ob->whatever && (ob->whatever == temp->whatever))// && // (ocl == tcl))// missiles with same owner // go through each other continue; if (!(ob->flags & FL_ISFIRE)) { int random; if (tcl != b_darkmonkobj) { MissileHit(ob,temp); ob->target = NULL; if (tcl == wallfireobj) MissileHit(temp,NULL); if (((ocl == p_kesobj) || (ocl == p_godballobj)) && (tcl < pillarobj)) continue; else return false; } random = GameRandomNumber("empower darkmonk",0); #if (SHAREWARE == 0) if (ocl == p_kesobj) { NewState(ob,&s_megaremove); //ob->state = NULL; temp->hitpoints += (((random>>3)+140)<<1); CAP_OSCUROS_HITPOINTS(temp); } else if (ocl == p_firebombobj) { NewState(ob,&s_explosion1); temp->hitpoints += (((random>>3)+70)<<1); CAP_OSCUROS_HITPOINTS(temp); ob->target = NULL; } else { NewState(ob,&s_explosion1); temp->hitpoints += (((random>>3)+50)<<1); CAP_OSCUROS_HITPOINTS(temp); ob->target = NULL; } temp->temp3 = ocl; temp->speed = 5*SPDPATROL; NewState(temp,&s_darkmonkreact); #endif return false; } else if (tcl < roboguardobj) { if ((temp->z == nominalheight) && (!((tcl == playerobj) && ((temp->flags & FL_GODMODE) || (temp->flags & FL_DOGMODE) || godmode)))) { if (tcl == playerobj) { playertype *pstate; objtype *owner = (objtype*)(ob->whatever); M_LINKSTATE(temp,pstate); if (temp->flags & FL_AV) { pstate->protectiontime = 1; if (temp==player) GM_UpdateBonus (pstate->protectiontime, false); continue; } //temp->flags &= ~FL_COLORED; pstate->health = 0; pstate->weapon = -1; if (owner->obclass == playerobj) BATTLE_PlayerKilledPlayer(battle_kill_with_missile,owner->dirchoosetime,temp->dirchoosetime); } temp->flags |= FL_SKELETON; temp->hitpoints = 0; Collision(temp,ob->whatever,-temp->momentumx,-temp->momentumy); } continue; } else { NewState(ob,&s_megaremove); ob->target = NULL; #if (SHAREWARE == 0) if (tcl == b_darkmonkobj) NewState(temp,&s_darkmonkfspark1); #endif } return false; } for (y=tileylow; y<=tileyhigh; y++) for (x=tilexlow; x<=tilexhigh; x++) { area = AREANUMBER(x,y); if (ValidAreanumber(area) && (areatried[area]==false)) { areatried[area] = true; goto actors; } } /******************* WALLS/PWALLS *****************************************/ walls: for (y=tileylow; y<=tileyhigh; y++) for (x=tilexlow; x<=tilexhigh; x++) { tempwall = (wall_t*)actorat[x][y]; wall=tilemap[x][y]; if (tempwall && M_ISWALL(tempwall) && (tempwall->which!=MWALL)) { if (ocl == h_mineobj) { if (WallCheck(ob->x-ob->momentumx, tryy)) { ob->momentumx = -ob->momentumx; continue; } else if (WallCheck(tryx, ob->y-ob->momentumy)) { ob->momentumy = -ob->momentumy; continue; } } //richocheting rockets stuff if ((ocl == p_bazookaobj || ocl == p_firebombobj || ocl == p_heatseekobj || ocl == p_drunkmissileobj || ocl == p_firewallobj || ocl == p_splitmissileobj || ocl == p_kesobj) && ricochetingRocketsEnabled) { if (WallCheck(ob->x-ob->momentumx, tryy)) { ob->momentumx = -ob->momentumx; int rand; rand = RandomNumber("Spawn Ricochet Sound",0); if (rand < 80) SD_PlaySoundRTP(SD_RICOCHET1SND,ob->x,ob->y); else if (rand < 160) SD_PlaySoundRTP(SD_RICOCHET2SND,ob->x,ob->y); else SD_PlaySoundRTP(SD_RICOCHET3SND,ob->x,ob->y); continue; } else if (WallCheck(tryx, ob->y-ob->momentumy)) { ob->momentumy = -ob->momentumy; int rand; rand = RandomNumber("Spawn Ricochet Sound",0); if (rand < 80) SD_PlaySoundRTP(SD_RICOCHET1SND,ob->x,ob->y); else if (rand < 160) SD_PlaySoundRTP(SD_RICOCHET2SND,ob->x,ob->y); else SD_PlaySoundRTP(SD_RICOCHET3SND,ob->x,ob->y); continue; } else { DetonateMissile(ob,tempwall); } } else { DetonateMissile(ob,tempwall); } //MissileHit(ob,tempwall); //return false; } tempstat = sprites[x][y]; sprrad = oldsrad; if (tempstat && ((tempstat->flags & FL_SHOOTABLE) || (tempstat->flags & FL_BLOCK))) { if ((tempstat->itemnumber >= stat_bcolumn) && (tempstat->itemnumber <= stat_icolumn)) sprrad += 0x5000; dx = tryx - tempstat->x; if ((dx < -sprrad) || (dx > sprrad)) continue; dy = tryy - tempstat->y; if ((dy < -sprrad) || (dy > sprrad)) continue; //#define MINSTATZDIFF 60 dzt = abs(ob->z - tempstat->z); dztp1 = abs(tryz - tempstat->z); /* if (ocl == p_firewallobj)// && (dztp1 <= MINSTATZDIFF)) { if (ob->flags & FL_ISFIRE) { int cz = (ob->z - tempstat->z + MINSTATZDIFF); if ((cz >= -MAXSTEPHEIGHT) && (cz <= 0)) { ob->z = tempstat->z - MINSTATZDIFF; tryz = ob->z + (ob->momentumz >> 16); dzt = MINSTATZDIFF; } } if ((dztp1 >= MINSTATZDIFF) || (dzt >= MINSTATZDIFF)) continue; if (!(ob->flags & FL_ISFIRE)) { StartFirewall(ob,tempstat->z - MINSTATZDIFF); return false; } } else*/ { if (dztp1 > 50) continue; DetonateMissile(ob,tempstat); } //MissileHit(ob,tempstat); //return false; } } //mwalls: if (M_ISMWALL(trytilex,trytiley)) { maskedwallobj_t * mw; wall=tilemap[trytilex][trytiley]; //tempwall = (wall_t*)actorat[trytilex][trytiley]; mw=maskobjlist[wall&0x3ff]; if (!(mw->flags&MW_BLOCKING)) { if ((levelheight > 1) && (((!(mw->flags & MW_ABOVEPASSABLE)) && (tryz <= 32)) || ((!(mw->flags & MW_MIDDLEPASSABLE)) && (tryz > 25) && (tryz < nominalheight-32)) || ((!(mw->flags & MW_BOTTOMPASSABLE)) && (tryz > maxheight - 74)) ) ) DetonateMissile(ob,NULL); } else if (mw->flags&MW_SHOOTABLE) { if (ob->z >= maxheight-64) { UpdateMaskedWall(tilemap[trytilex][trytiley]&0x3ff); } else DetonateMissile(ob,NULL); } else DetonateMissile(ob,NULL); //MissileHit(ob,tempwall); //return false; } /******************* DOOR STUFF ******************************************/ else if (M_ISDOOR(trytilex,trytiley)) { doorn = tilemap[trytilex][trytiley]; tempdoor = doorobjlist[doorn&0x3ff]; if (tempdoor->position>=0x8000) { if (ob->z>maxheight-64) return true; } DetonateMissile(ob,tempdoor); } return true; } void SpawnFirewall(objtype*ob,int which,int newz) { int i,j,count,newx,newy; objtype* owner; wall_t*tempwall; statetype* frame; int offset; owner = (objtype*)(ob->whatever); if ((owner->temp1 < 2) && (MISCVARS->firespawned < 14)) { for(i=0; i<=which; i++) { GetNewActor (); MakeActive(new); MISCVARS->firespawned ++; new->obclass = p_firewallobj; new->which = ACTOR; new->areanumber = ob->areanumber; MakeLastInArea(new); offset = 0x6000; if (!which) new->temp1 = ob->temp1; else if (i==1) new->temp1 = ob->angle + ANGLES/4; else if (i==2) new->temp1 = ob->angle - ANGLES/4; else { new->temp1 = 0; offset = 0; new->flags |= FL_DONE; } Fix(new->temp1); new->speed = 0x8000; new->angle = ob->angle; ParseMomentum(new,new->angle); newx = ob->x + FixedMul(offset,costable[new->temp1]); newy = ob->y - FixedMul(offset,sintable[new->temp1]); SetFinePosition(new,newx,newy); SetVisiblePosition(new,newx,newy); new->whatever = ob->whatever; new->dirchoosetime = 2; new->flags |= (FL_NEVERMARK|FL_ABP|FL_NOFRICTION); count = (int)(GameRandomNumber("SpawFireWall",0) & 15); for(frame = &s_fireunit1,j=0; j<count; frame = frame->next,j++); NewState(new,frame); new->flags |= FL_ISFIRE; //SD_Play(SD_EXPL); tempwall = (wall_t*)actorat[new->tilex][new->tiley]; new->z = newz; if (tempwall && M_ISWALL(tempwall)) { SetFinePosition(new,ob->x,ob->y); SetVisiblePosition(new,ob->x,ob->y); owner->temp1++; } } } } void T_Firethink(objtype*ob) { if (ob->dirchoosetime) ob->dirchoosetime--; else if (!(ob->flags & FL_DONE)) { SpawnFirewall(ob,0,ob->z); ob->flags |= FL_DONE; } MissileMovement(ob); } void ResolveRide(objtype *ob) { objtype *ride = (objtype*)(ob->whatever); if (M_ISACTOR(ride) && (ob->obclass != playerobj)) { if (ob->flags & FL_RIDING) { int dx,dy; dx = ob->x - ride->x; dy = ob->y - ride->y; if ((dx < -MINACTORDIST) || (dx > MINACTORDIST) || (dy < -MINACTORDIST) || (dy > MINACTORDIST) ) { ride->whatever = NULL; ob->whatever = NULL; ob->flags &= ~FL_RIDING; } } } } void MoveActor(objtype*ob) { int oldarea,newarea, tilex,tiley,oldtilex,oldtiley; ResolveRide(ob); oldtilex = ob->tilex; oldtiley = ob->tiley; SetFinePosition(ob,ob->x+ob->momentumx,ob->y+ob->momentumy); /* if (ob->state == &s_explosion1) Error("moving explosion"); */ if ((ob->obclass == playerobj) || (ob->flags & FL_NOFRICTION) || (ob->state->think == T_Collide) || (ob->obclass == b_heinrichobj) || (ob->obclass == h_mineobj)) SetVisiblePosition(ob,ob->x,ob->y); if (ob->obclass == inertobj) return; if ((ob->obclass == b_darksnakeobj) && (ob != SNAKEHEAD)) { oldarea = ob->areanumber; newarea = SNAKEHEAD->areanumber; if (oldarea != newarea) { RemoveFromArea(ob); ob->areanumber = newarea; MakeLastInArea(ob); } return; } oldarea = ob->areanumber; newarea = AREANUMBER(ob->tilex,ob->tiley); if (!(ob->flags & (FL_NONMARK|FL_NEVERMARK))) { if ((oldtilex != ob->tilex) || (oldtiley != ob->tiley)) { if (actorat[oldtilex][oldtiley] == (void*)ob) actorat[oldtilex][oldtiley] = NULL; if (actorat[ob->tilex][ob->tiley]) { objtype* temp; temp = (objtype*)actorat[ob->tilex][ob->tiley]; if (temp->which != SPRITE) actorat[ob->tilex][ob->tiley] = ob; } else actorat[ob->tilex][ob->tiley] = ob; } } #define CheckAdjacentArea(x,y) \ { \ if (InMapBounds(x,y)) \ { \ temparea = AREANUMBER(x,y); \ if (ValidAreanumber(temparea)) \ newarea = temparea; \ } \ } if (!ValidAreanumber(newarea)) //find empty tile { int temparea; tilex = ob->tilex; tiley = ob->tiley; CheckAdjacentArea(tilex+1,tiley); CheckAdjacentArea(tilex-1,tiley); CheckAdjacentArea(tilex,tiley+1); CheckAdjacentArea(tilex,tiley-1); } //Error("new area invalid for actor %d, class %d", // ob-&objlist[0],ob->obclass); //====================== swap in linked lists ===================== if (oldarea != newarea) { RemoveFromArea(ob); ob->areanumber = newarea; MakeLastInArea(ob); } } void SpawnPushColumn(int tilex,int tiley,int which,int dir, int linked) { if (which==0) { SpawnNewObj(tilex,tiley,&s_pushcolumn1,pillarobj); // for (i=0;i<(levelheight-1);i++) // SpawnStatic(tilex,tiley,stat_bcolumn,-(i<<6)); } else if (which==1) { SpawnNewObj(tilex,tiley,&s_pushcolumn2,pillarobj); // for (i=0;i<(levelheight-1);i++) // SpawnStatic(tilex,tiley,stat_gcolumn,-(i<<6)); } else { SpawnNewObj(tilex,tiley,&s_pushcolumn3,pillarobj); // for (i=0;i<(levelheight-1);i++) // SpawnStatic(tilex,tiley,stat_icolumn,-(i<<6)); } PreCacheActor(pillarobj,which); gamestate.secrettotal++; new->speed = PILLARMOM; new->temp1 = 0x20000; new->temp2 = linked; new->flags |= (FL_BLOCK|FL_NOFRICTION); new->flags &= ~FL_SHOOTABLE; new->flags |= FL_HEIGHTFLIPPABLE; new->dir = dir; if (dir != nodir) ParseMomentum(new,dirangle8[new->dir]); } void SpawnWallfire(int tilex, int tiley, int dir) { int offx,offy; GetNewActor(); new->speed = 0x2000; SetTilePosition(new,tilex,tiley); SetVisiblePosition(new,new->x,new->y); new->obclass = wallfireobj; new->dir = dir*2; new->flags |= (FL_BLOCK|FL_NOFRICTION|FL_NEVERMARK); new->flags &= ~FL_SHOOTABLE; new->which = ACTOR; new->angle = dirangle8[new->dir]; offx = FixedMul(0x10000,costable[new->angle])>>TILESHIFT; offy = -(FixedMul(0x10000,sintable[new->angle])>>TILESHIFT); new->areanumber = MAPSPOT(new->tilex+offx,new->tiley+offy,0)-AREATILE; MakeLastInArea(new); NewState(new,&s_wallfireball); } void SpawnSneaky(int tilex,int tiley) { SpawnNewObj(tilex,tiley,&s_sneakydown,lowguardobj); new->temp3 = SNEAKY; if (!loadedgame) gamestate.killtotal++; StandardEnemyInit(new,north>>1); PreCacheActor(lowguardobj,0); } void RespawnEluder(void) { int rand,count; int nx,ny; rand = (GameRandomNumber("eluder respawn",0) % NUMSPAWNLOCATIONS); for(count=0; count < NUMSPAWNLOCATIONS; count++) { if (!actorat[SPAWNLOC[rand].x][SPAWNLOC[rand].y]) { SpawnCollector(SPAWNLOC[rand].x,SPAWNLOC[rand].y); return; } rand= ((rand + 1) % NUMSPAWNLOCATIONS); } nx = SPAWNLOC[rand].x; ny = SPAWNLOC[rand].y; FindEmptyTile(&nx,&ny); SpawnCollector(nx,ny); } //**************************************************************************** // // // //**************************************************************************** void SpawnCollector(int tilex, int tiley) { #if (SHAREWARE == 0) if ( dopefish==true ) { SpawnNewObj(tilex,tiley,&s_scottwander1,collectorobj); } else #endif { SpawnNewObj(tilex,tiley,&s_collectorwander1,collectorobj); } new->flags |= (FL_SHOOTABLE|FL_BLOCK|FL_NOFRICTION|FL_FULLLIGHT); new->hitpoints = 500; new->speed = 0x3000; new->dir = north; new->dirchoosetime = 175; new->z = PlatformHeight(tilex,tiley); if (new->z == -10) new->z = 0; if (areabyplayer[new->areanumber]) { new->flags |= FL_ABP; MakeActive(new); } } void SelectDoorDir(objtype*ob) { int dx,dy,actrad; dirtype dtry1,dtry2,tdir,olddir; dx= ob->targettilex - ob->x; dy= ob->y - ob->targettiley; olddir = ob->dir; if ((abs(dx) < 0x4000) && (abs(dy) < 0x4000)) { ZEROMOM; SetFinePosition(ob,ob->targettilex,ob->targettiley); SetVisiblePosition(ob,ob->x,ob->y); ParseMomentum(ob,dirangle8[ob->temp2]); ActorMovement(ob); ob->temp2 = 0; ob->temp1 = 20; #if (SHAREWARE == 0) if ( dopefish==true ) { NewState(ob,&s_scottwander1); } else #endif { NewState(ob,&s_collectorwander1); } ob->targettilex = ob->targettiley = 0; ob->dirchoosetime = 165; return; } ZEROMOM; ParseMomentum(ob,atan2_appx(dx,dy)); ActorMovement(ob); if (ob->momentumx || ob->momentumy) return; actrad = ACTORSIZE; dtry1=nodir; dtry2=nodir; if (dx> actrad) dtry1= east; else if (dx< -actrad) dtry1= west; if (dy> actrad) dtry2= north; else if (dy < -actrad) dtry2= south; if (abs(dy)>abs(dx)) { tdir=dtry1; dtry1=dtry2; dtry2=tdir; } if (dtry1 != nodir) M_CHECKDIR(ob,dtry1); if (dtry2 != nodir) M_CHECKDIR(ob,dtry2); if (dtry1 != nodir) M_CHECKDIR(ob,dirorder[dtry1][NEXT]); if (dtry2 != nodir) M_CHECKDIR(ob,dirorder[dtry2][NEXT]); for(tdir = dirorder[olddir][NEXT]; tdir != olddir; tdir = dirorder[tdir][NEXT]) M_CHECKDIR(ob,tdir); ob->dir = olddir; } boolean EluderCaught(objtype*ob) { objtype *temp; int dx,dy,dz; playertype *pstate; int dist = 0xc000; for(temp = PLAYER[0]; temp != PLAYER[numplayers-1]->next; temp=temp->next) { #if (SHAREWARE == 0) if (temp->state != &s_doguse) continue; #endif dx = M_ABS(temp->x - ob->x); if (dx > dist) continue; dy = M_ABS(temp->y - ob->y); if (dy > dist) continue; dz = M_ABS(temp->z - ob->z); if (dz > (dist>>10)) continue; M_LINKSTATE(temp,pstate); //if (DOGSCRATCH.attackinfo[pstate->attackframe].attack == at_pulltrigger) { BATTLE_CheckGameStatus(battle_caught_eluder,temp->dirchoosetime); SpawnNewObj(ob->tilex,ob->tiley,&s_itemspawn1,inertobj); new->flags |= FL_ABP; SetFinePosition(new,ob->x,ob->y); SetVisiblePosition(new,ob->x,ob->y); new->z = ob->z; SD_PlaySoundRTP(SD_GETBONUSSND,ob->x,ob->y); MakeActive(new); NewState(ob,&s_megaremove); return true; } } return false; } void T_CollectorFindDoor(objtype*ob) { if (EluderCaught(ob)) return; if (!(gamestate.TimeCount % 17)) SD_PlaySoundRTP(SD_MONKGRABSND,ob->x,ob->y); if ((ob->z != nominalheight) && (!IsPlatform(ob->tilex,ob->tiley))) ZEROMOM; if (ob->dirchoosetime) ob->dirchoosetime --; else { #if (SHAREWARE == 0) if ( dopefish==true ) { NewState(ob,&s_scottwander1); } else #endif { NewState(ob,&s_collectorwander1); } ob->dirchoosetime = 165; ob->targettilex = ob->targettiley = 0; return; } if (ob->temp1) { int dx,dy; ob->temp1 --; ActorMovement(ob); dx = ob->targettilex-ob->x; dy = ob->targettiley-ob->y; if ((abs(dx) < 0x4000) && (abs(dy) < 0x4000)) { ZEROMOM; SetFinePosition(ob,ob->targettilex,ob->targettiley); SetVisiblePosition(ob,ob->x,ob->y); ParseMomentum(ob,dirangle8[ob->temp2]); ActorMovement(ob); ob->temp2 = 0; ob->temp1 = 35; #if (SHAREWARE == 0) if ( dopefish==true ) { NewState(ob,&s_scottwander1); } else #endif { NewState(ob,&s_collectorwander1); } ob->targettilex = ob->targettiley = 0; ob->dirchoosetime = 165; return; } if (NOMOM) ob->temp1 = 0; return; } ob->temp1 = 5; if (ob->targettilex || ob->targettiley) SelectDoorDir(ob); else { int i; doorobj_t* dptr; //========================================================================== #define SetCollectorTarget(xoffset,yoffset,newdir) \ { \ ob->targettilex = ((dptr->tilex + (xoffset)) << TILESHIFT) + HALFGLOBAL1; \ ob->targettiley = ((dptr->tiley + (yoffset)) << TILESHIFT) + HALFGLOBAL1; \ ob->temp2 = newdir; \ if (GameRandomNumber("collector door search",0) < 100) \ return; \ } //========================================================================== for(i=0; i<doornum; i++) { dptr = doorobjlist[i]; if (dptr->vertical) { int area1 = AREANUMBER(dptr->tilex-1,dptr->tiley), area2 = AREANUMBER(dptr->tilex+1,dptr->tiley); if (area1 == ob->areanumber) SetCollectorTarget(-1,0,east) else if (area2 == ob->areanumber) SetCollectorTarget(1,0,west); } else { int area1 = AREANUMBER(dptr->tilex,dptr->tiley-1), area2 = AREANUMBER(dptr->tilex,dptr->tiley+1); if (area1 == ob->areanumber) SetCollectorTarget(0,-1,south) else if (area2 == ob->areanumber) SetCollectorTarget(0,1,north); } } } } void T_CollectorWander(objtype*ob) { int newtilex,newtiley; if (EluderCaught(ob)) return; if ((ob->z != nominalheight) && (!IsPlatform(ob->tilex,ob->tiley))) ZEROMOM; if (!(gamestate.TimeCount & 15))//%17 SD_PlaySoundRTP(SD_MONKGRABSND,ob->x,ob->y); if (ob->dirchoosetime) { if (doornum > 0) ob->dirchoosetime --; } else { #if (SHAREWARE == 0) if ( dopefish==true ) { NewState(ob,&s_scottwanderdoor1); } else #endif { NewState(ob,&s_collectorfdoor1); } ob->temp1 = 0; ob->dirchoosetime = 165; ob->targettilex = ob->targettiley = 0; return; } if (ob->temp1) // temp1 holds direction time ob->temp1 --; else { dirtype bestdir,tempdir; bestdir = angletodir[GameRandomNumber("collector theta",0) << 3]; for(tempdir = bestdir; tempdir != dirorder[bestdir][PREV]; tempdir = dirorder[tempdir][NEXT]) { ParseMomentum(ob,dirangle8[tempdir]); newtilex = ((ob->x + ob->momentumx)>>16); newtiley = ((ob->y + ob->momentumy)>>16); if (IsWindow(newtilex,newtiley)) continue; ActorMovement(ob); if (ob->momentumx || ob->momentumy) { ob->temp1 = (GameRandomNumber("collector choose time",0) >> 2); return; } } } newtilex = ((ob->x + ob->momentumx)>>16); newtiley = ((ob->y + ob->momentumy)>>16); if (IsWindow(newtilex,newtiley)) { ob->temp1 = 0; return; } ActorMovement(ob); if (NOMOM) ob->temp1 = 0; } boolean CheckDoor(objtype *ob,doorobj_t * door,int trytilex,int trytiley) { boolean doorok=false; switch(ob->dir) { case north: if ((ob->tiley == (door->tiley + 1)) && (trytilex == ob->tilex)) doorok = true; break; case east: if ((ob->tilex == (door->tilex - 1)) && (trytiley == ob->tiley)) doorok = true; break; case south: if ((ob->tiley == (door->tiley - 1)) && (trytilex == ob->tilex)) doorok = true; break; case west: if ((ob->tilex == (door->tilex + 1)) && (trytiley == ob->tiley)) doorok = true; break; default: ; } if (doorok) { SetTilePosition(ob,ob->tilex,ob->tiley); SetVisiblePosition(ob,ob->x,ob->y); return true; } return false; } boolean WallCheck(int tryx,int tryy) { int tilexlow,tilexhigh,tileylow,tileyhigh,y,x; tilexlow = (int)((tryx -PLAYERSIZE) >>TILESHIFT); tileylow = (int)((tryy -PLAYERSIZE) >>TILESHIFT); tilexhigh = (int)((tryx + PLAYERSIZE) >>TILESHIFT); tileyhigh = (int)((tryy + PLAYERSIZE) >>TILESHIFT); for (y=tileylow; y<=tileyhigh; y++) for (x=tilexlow; x<=tilexhigh; x++) { //tempwall = (wall_t*)actorat[x][y]; //if (tempwall && M_ISWALL(tempwall)) if (tilemap[x][y]) return false; } return true; } boolean QuickSpaceCheck(objtype*ob,int tryx, int tryy) { int xlow,xhigh,ylow,yhigh,x,y,dx,dy; objtype* temp; xlow = (int)((tryx-ACTORSIZE) >>TILESHIFT); ylow = (int)((tryy-ACTORSIZE) >>TILESHIFT); xhigh = (int)((tryx+ACTORSIZE) >>TILESHIFT); yhigh = (int)((tryy+ACTORSIZE) >>TILESHIFT); /******************* WALLS/PWALLS *****************************************/ for (y=ylow; y<=yhigh; y++) for (x=xlow; x<=xhigh; x++) { temp = (objtype*)actorat[x][y]; if ((temp && (temp->which != ACTOR)) || (sprites[x][y] && (sprites[x][y]->flags & FL_BLOCK)) || tilemap[x][y]) return false; } for(temp=firstareaactor[ob->areanumber]; temp; temp=temp->nextinarea) { if (temp == ob) continue; if ((temp->flags & FL_NONMARK) || (temp->flags & FL_NEVERMARK)) continue; dx = tryx - temp->x; if ((dx < -MINACTORDIST) || (dx > MINACTORDIST)) continue; dy = tryy - temp->y; if ((dy < -MINACTORDIST) || (dy > MINACTORDIST)) continue; if (ob->whatever == (void*)temp) continue; if (temp->whatever == ob->whatever) continue; return false; } return true; } //========================================================================= // // ACTOR TRY MOVE MADNESS // //========================================================================= typedef enum { NO_MOVEMENT, Z_MOVEMENT_ONLY, OK_TO_CONTINUE } movement_status; //==================== Some ActorTryMove macros ============================== #define CheckProximitySpecials(ob,temp) \ { \ if (ocl == b_heinrichobj) \ { \ if (tcl == playerobj) \ { \ playertype *pstate; \ \ M_LINKSTATE(temp,pstate); \ DamageThing(temp,5); \ temp->whatever = ob; \ temp->temp2 = COLUMNCRUSH; \ pstate->heightoffset += 4; \ if (pstate->heightoffset >= 30) \ pstate->heightoffset = 30; \ pstate->oldheightoffset = pstate->heightoffset; \ } \ else \ { \ temp->momentumx = temp->momentumy = temp->momentumz = 0; \ temp->hitpoints = 0; \ } \ if (temp->hitpoints <= 0) \ temp->flags |= FL_HBM; \ Collision(temp,ob,0,0); \ continue; \ } \ \ else if ((ocl == b_darksnakeobj) && (tcl == playerobj)) \ { \ DamageThing(temp,1); \ Collision(temp,ob,0,0); \ M_CheckPlayerKilled(temp); \ } \ \ if ((ocl == boulderobj) && (tcl >= lowguardobj) && (tcl < roboguardobj))\ {temp->momentumx = temp->momentumy = temp->momentumz = 0; \ temp->hitpoints = 0; \ temp->flags |= FL_HBM; \ Collision(temp,ob,0,0); \ SD_PlaySoundRTP(SD_ACTORSQUISHSND,temp->x,temp->y); \ continue; \ } \ \ if (pusher && (ocl != tcl) && (!(temp->flags & FL_DYING)) && \ (tcl < roboguardobj) \ ) \ {if ((!ob->ticcount) && (ocl != collectorobj) && (ocl != diskobj))\ DamageThing(temp,5); \ \ if (tcl == playerobj) \ temp->flags |= FL_PUSHED; \ Collision(temp,ob,ob->momentumx-temp->momentumx,ob->momentumy-temp->momentumy);\ M_CheckPlayerKilled(temp); \ continue; \ } \ \ if (bouncer) \ {ob->momentumx = -ob->momentumx; \ continue; \ } \ } #define CheckStepping(ob,step,minzdiff) \ { \ int cz = (ob->z - step->z + minzdiff); \ \ if ((cz >= -MAXSTEPHEIGHT) && (cz <= MAXSTEPHEIGHT)) \ {if ((ob->obclass == playerobj) && (ob->temp2 == 0) && \ (ob->z != (step->z - minzdiff)) \ ) \ { \ playertype *pstate; \ \ M_LINKSTATE(ob,pstate); \ \ pstate->heightoffset = pstate->oldheightoffset + cz; \ ob->temp2 = (cz >= 0)?(STEPUP):(STEPDOWN); \ } \ ob->z = step->z - minzdiff; \ tryz = ob->z + (ob->momentumz >> 16); \ dzt = minzdiff; \ } \ } \ //============ Players crushing other players ===================== void BattleCrushCheck(objtype *ob,objtype *listrover) \ { if ((ob->obclass == playerobj) && (listrover->obclass == playerobj)) { playertype * pstate; M_LINKSTATE(listrover,pstate); if (pstate->health <= 0) BATTLE_PlayerKilledPlayer(battle_kill_by_crushing,ob->dirchoosetime, listrover->dirchoosetime); } } //================================================================= movement_status CheckOtherActors(objtype*ob,int tryx,int tryy,int tryz) { objtype *listrover; int area; int op; int areatried[NUMAREAS]= {0}; int tilexlow,tilexhigh,tileylow,tileyhigh; int radius,actrad,oldrad; boolean bouncer,pusher,thinkingactor,zstoppable,ACTORSTOP; int dzt,dztp1,checkz; int x,y,dx,dy; int ocl,tcl; ocl = ob->obclass; actrad = MINACTORDIST;//ACTORSIZE+0x2800; pusher = ((ocl == wallopobj) || (ocl == pillarobj) || (ocl == roboguardobj) || (ocl == collectorobj) || (ocl == boulderobj) || (ocl == diskobj) ); thinkingactor = ((ocl != playerobj) && (ob->state->think != T_Collide) && (ocl < roboguardobj) ); zstoppable = (!(ob->flags & FL_DYING)); bouncer = ((ocl == playerobj) && (ob->flags & FL_ELASTO)); radius = ACTORSIZE; if (ocl != playerobj) { //actrad = MINACTORDIST; //if ((ob->dir == nodir) && (ocl != b_robobossobj) && // (ocl != wallopobj) && (ocl != roboguardobj) && (ocl != diskobj) // ) // Error("ob called with nodir"); if (ocl == boulderobj) radius += (ACTORSIZE/4); else if (ocl == b_darksnakeobj) radius -= 6000; else if (ocl == inertobj) radius -= 0x2000; } tilexlow = (int)((tryx-radius) >>TILESHIFT); tileylow = (int)((tryy-radius) >>TILESHIFT); tilexhigh = (int)((tryx+radius) >>TILESHIFT); tileyhigh = (int)((tryy+radius) >>TILESHIFT); area = ob->areanumber; areatried[area] = 1; ACTORSTOP = false; oldrad = actrad; actors: for(listrover=firstareaactor[area]; listrover; listrover=listrover->nextinarea) { actrad = oldrad; if (listrover == ob) continue; tcl = listrover->obclass; if ((tcl == b_darksnakeobj) && (listrover != SNAKEHEAD)) continue; if (((tcl == bladeobj) || (tcl == firejetobj)) && thinkingactor) actrad += 0x3000; dx = tryx - listrover->x; if ((dx < -actrad) || (dx > actrad)) continue; dy = tryy - listrover->y; if ((dy < -actrad) || (dy > actrad)) continue; if ((ocl == b_darksnakeobj) && (tcl == ocl)) continue; if ((tcl == springobj) && (listrover->state->condition & SF_UP) && (listrover->temp1!=3) && (levelheight > 1) && (abs(listrover->z - ob->z) < 5) && (!ob->momentumz) ) { { op = (FixedMul((int)GRAVITY,(int)((ob->z-10)<<16))<<1); ob->momentumz = -FixedSqrtHP(op); } SD_PlaySoundRTP(SD_SPRINGBOARDSND,listrover->x,listrover->y); NewState(listrover,&s_spring2); } if ((tcl == firejetobj) && (ob->z < listrover->z)) continue; if ((!(listrover->flags & FL_BLOCK)) && (actrad == oldrad)) // if not blocking // and actor not avoiding // env. danger continue; if (tcl == crushcolobj) checkz = listrover->temp2; else checkz = listrover->z; #define MINACTORZDIFF 58 dzt = abs(checkz - ob->z); dztp1 = abs(checkz - tryz); if ((tcl == diskobj) && (dztp1 <= MINACTORZDIFF) && zstoppable && (ocl != b_heinrichobj) ) CheckStepping(ob,listrover,MINACTORZDIFF); dztp1 = abs(checkz - tryz); if ((dzt > (MINACTORZDIFF - 25)) && (dzt < MINACTORZDIFF) && (dztp1 < MINACTORZDIFF) && (tcl < roboguardobj) && (ocl < roboguardobj) ) { int rdx,rdy; rdx = abs(ob->x - listrover->x); rdy = abs(ob->y - listrover->y); if ((rdx < actrad) && (rdy < actrad)) { if (ob->z > listrover->z) listrover->z = ob->z - MINACTORZDIFF; else ob->z = listrover->z - MINACTORZDIFF; dzt = dztp1 = MINACTORZDIFF; } } if ((dztp1 >= MINACTORZDIFF) || (dzt >= MINACTORZDIFF)) { if ((dzt >= MINACTORZDIFF) && (dztp1 <= MINACTORZDIFF) && zstoppable ) { //ob->momentumz = 0; if (ob->z < listrover->z) { ob->z = listrover->z - MINACTORZDIFF; ob->momentumz = 0; } else ob->momentumz = 2*GRAVITY; if ((listrover->z > ob->z) && (tcl < roboguardobj) && (ocl < roboguardobj) && (!(listrover->flags & FL_DYING)) ) { DamageThing(listrover,5); BattleCrushCheck(ob,listrover); Collision(listrover,ob,listrover->momentumx,listrover->momentumy); /* if ((ocl == playerobj) && (listrover->flags & FL_DYING)) GivePoints(starthitpoints[gamestate.difficulty][tcl]); */ } if (((tcl == bladeobj) || (tcl == diskobj)) && (ob->z < listrover->z)) { ob->whatever = listrover; if (listrover->flags & FL_ACTIVE) ob->flags |= FL_RIDING; listrover->whatever = ob; } //Debug("\nplayerz %d, tryz %d momz zeroed at %d, clearances %d and %d", // ob->z,tryz,listrover->z-64 + (listrover->momentumz >> 16),dzt,dztp1); } continue; } CheckProximitySpecials(ob,listrover); ACTORSTOP = true; if (!ob->momentumz) return NO_MOVEMENT; } for (y=tileylow; y<=tileyhigh; y++) for (x=tilexlow; x<=tilexhigh; x++) { area = AREANUMBER(x,y); if (ValidAreanumber(area) && (areatried[area]==0)) { areatried[area] = 1; goto actors; } } if (ACTORSTOP==true) return Z_MOVEMENT_ONLY; return OK_TO_CONTINUE; } movement_status CheckRegularWalls(objtype *ob,int tryx,int tryy,int tryz) { int tilexlow,tilexhigh,tileylow,tileyhigh,x,y,radius; classtype ocl; boolean ISPLAYER=false; ocl = ob->obclass; tryz=tryz; if (ocl != playerobj) { radius = ACTORSIZE - 0x1000; //actrad = MINACTORDIST; //if ((ob->dir == nodir) && (ocl != b_robobossobj) && // (ocl != wallopobj) && (ocl != roboguardobj) && (ocl != diskobj) // ) // Error("ob called with nodir"); if (ocl == boulderobj) radius += (ACTORSIZE/4); else if (ocl == b_darksnakeobj) radius -= 6000; else if (ocl == inertobj) radius -= 0x2000; } else { radius = PLAYERSIZE; ISPLAYER = true; } tilexlow = (int)((tryx-radius) >>TILESHIFT); tileylow = (int)((tryy-radius) >>TILESHIFT); tilexhigh = (int)((tryx+radius) >>TILESHIFT); tileyhigh = (int)((tryy+radius) >>TILESHIFT); for (y=tileylow; y<=tileyhigh; y++) for (x=tilexlow; x<=tilexhigh; x++) { wall_t *tempwall; tempwall = (wall_t*)actorat[x][y]; if (tempwall) { if (tempwall->which==WALL)// && IsWindow(x,y)==false) { if (ocl == boulderobj) { #if (SHAREWARE == 0) NewState(ob,&s_bouldersink1); #endif SD_PlaySoundRTP(SD_BOULDERHITSND,ob->x,ob->y); } else if (ISPLAYER && (!(ob->flags & FL_DYING)) && (!(ob->flags & FL_AV)) && (tempwall->flags & FL_W_DAMAGE)) { DamageThing(ob,5); Collision(ob,(objtype*)tempwall,0,0); M_CheckPlayerKilled(ob); SD_PlaySoundRTP(SD_PLAYERBURNEDSND,ob->x,ob->y); } //return false; if ((ocl == inertobj) && (ob->dirchoosetime == GIBVALUE) && (((ob->tilex - x) == 0) != ((ob->tiley - y) == 0)) && (ob->z > -28) ) { // SoftError ("Blood Dripping oldpolltime=%ld\n",oldpolltime); BloodDrip(ob,x,y); return NO_MOVEMENT; } if (!ob->momentumz) return NO_MOVEMENT; else// if (ocl != inertobj) return Z_MOVEMENT_ONLY; //else //goto doors; } else if (tempwall->which==PWALL) { pwallobj_t*pw; int dx,dy; pw=(pwallobj_t *)tempwall; dx = abs(pw->x - tryx); if (dx > PWALLRAD+0x5000) continue; dy = abs(pw->y - tryy); if (dy > PWALLRAD+0x5000) continue; return NO_MOVEMENT; } } } return OK_TO_CONTINUE; } movement_status CheckStaticObjects(objtype *ob,int tryx,int tryy,int tryz) { int dx,dy,dzt,dztp1,x,y; statobj_t*tempstat; int sprrad,oldsrad,sprtrad; boolean specialstat=false,widestat=false,zstoppable; int sprxlow,sprxhigh,sprylow,spryhigh; boolean SPRSTOP; classtype ocl; ocl = ob->obclass; if (ocl != playerobj) sprtrad = ACTORSIZE - 0x1000; else sprtrad = ACTORSIZE - 0x1000 + 0x10000; sprxlow = (int)((tryx-sprtrad) >>TILESHIFT); sprylow = (int)((tryy-sprtrad) >>TILESHIFT); sprxhigh = (int)((tryx+sprtrad) >>TILESHIFT); spryhigh = (int)((tryy+sprtrad) >>TILESHIFT); if (sprxlow < 0) sprxlow = 0; if (sprxhigh > (MAPSIZE-1)) sprxhigh = MAPSIZE-1; if (sprylow < 0) sprylow = 0; if (spryhigh > (MAPSIZE-1)) spryhigh = MAPSIZE-1; SPRSTOP = false; sprrad = 0x4500; zstoppable = (!(ob->flags & FL_DYING)); oldsrad = sprrad; for (y=sprylow; y<=spryhigh; y++) for (x=sprxlow; x<=sprxhigh; x++) { tempstat = sprites[x][y]; sprrad = oldsrad; if (tempstat) { specialstat = ((tempstat->itemnumber == stat_heatgrate) || (tempstat->itemnumber == stat_pit) ); widestat = (((tempstat->itemnumber >= stat_bcolumn) && (tempstat->itemnumber <= stat_icolumn)) || (tempstat->itemnumber == stat_disk) ); if ((tempstat->flags & FL_BLOCK) || (specialstat==true)) { if ((specialstat==true) && (ocl !=playerobj) && (ob->state->think != T_Collide) ) sprrad += 0x5000; if (widestat==true) sprrad += 0x3b00; if ((tempstat->itemnumber == stat_ironbarrel) || (tempstat->itemnumber == stat_bonusbarrel)) sprrad += 0x5000; dx = abs(tryx - tempstat->x); if (dx > sprrad) continue; dy = abs(tryy - tempstat->y); if (dy > sprrad) continue; #define MINSTATZDIFF 58 dzt = abs(ob->z - tempstat->z); dztp1 = abs(tryz - tempstat->z); if (widestat && (dztp1 <= MINSTATZDIFF) && zstoppable && (ocl != b_heinrichobj) ) CheckStepping(ob,tempstat,MINSTATZDIFF); dztp1 = abs(tryz - tempstat->z); #if (SHAREWARE == 0) if ((ocl == b_darksnakeobj) && (tempstat->itemnumber == stat_heatgrate)) { if (ob->state->think == T_DarkSnakeChase) NewState(ob,&s_darkmonkredhead); else NewState(ob,&s_darkmonkredlink); ob->temp3 ++; // make shootable } #endif if (specialstat==true) continue; if ((dztp1 >= MINSTATZDIFF) || (dzt >= MINSTATZDIFF)) { if ((dzt >= MINSTATZDIFF) && (dztp1 <= MINSTATZDIFF) && zstoppable) { //ob->momentumz = 0; if (ob->z <= tempstat->z) { ob->z = tempstat->z - MINSTATZDIFF; ob->momentumz = 0; } else ob->momentumz = 2*GRAVITY; // ((2*GRAVITY + GRAVITY) >> 16) = 1 } continue; } if (ocl == boulderobj) { if ((tempstat->itemnumber < stat_bcolumn) || (tempstat->itemnumber > stat_icolumn) ) { tempstat->flags |= FL_SHOOTABLE; DamageThing(tempstat,tempstat->hitpoints); continue; } #if (SHAREWARE == 0) else NewState(ob,&s_bouldersink1); #endif } //ob->momentumz=0; //return false; SPRSTOP=true; if (!ob->momentumz) return NO_MOVEMENT; } } } if (SPRSTOP == true) return Z_MOVEMENT_ONLY; return OK_TO_CONTINUE; } //============== Platform craziness ====================================== #define ClipHeight(ob,clipz) \ { ob->momentumz = 0; \ \ if (ISPLAYER && (ob->z != clipz) && (ob->temp2 == 0)) \ {playertype *pstate; \ int dz = ob->z - clipz; \ \ M_LINKSTATE(ob,pstate); \ \ pstate->heightoffset = pstate->oldheightoffset + dz; \ ob->temp2 = (dz >= 0)?(STEPUP):(STEPDOWN); \ } \ \ ob->z = clipz; \ } //====================== #define CheckSpecialGibMovement(blocker) \ { \ int centerx = ((trytilex<<16) + 0x8000); \ int centery = ((trytiley<<16) + 0x8000); \ \ if (blocker->vertical==false) \ { \ int dyt = centery - ob->y; \ int dytp1 = centery - tryy; \ \ if ((abs(dytp1) > abs(dyt)) && \ (SGN(dyt) == SGN(dytp1)) \ ) \ return OK_TO_CONTINUE; \ \ } \ else \ { \ int dxt = centerx - ob->x; \ int dxtp1 = centerx - tryx; \ \ if ((abs(dxtp1) > abs(dxt)) && \ (SGN(dxt) == SGN(dxtp1)) \ ) \ return OK_TO_CONTINUE; \ \ } \ } movement_status CheckMaskedWalls(objtype *ob,int tryx,int tryy,int tryz) { int trytilex,trytiley; boolean MWALLSTOP; int ISPLAYER = (ob->obclass == playerobj); classtype ocl = ob->obclass; trytilex = (tryx >> TILESHIFT); trytiley = (tryy >> TILESHIFT); MWALLSTOP = false; //for (y=tileylow;y<=tileyhigh;y++) // for (x=tilexlow;x<=tilexhigh;x++) if (M_ISMWALL(trytilex,trytiley)) { int wall = tilemap[trytilex][trytiley]; maskedwallobj_t * mw; mw=maskobjlist[wall&0x3ff]; if (ocl == inertobj) CheckSpecialGibMovement(mw); if (!(mw->flags&MW_BLOCKING)) { if (mw->flags&MW_NONDOGBLOCKING) { if ((ocl==playerobj)&&(ob->flags&FL_DOGMODE)) { if (ob->z < nominalheight) { MWALLSTOP = true; if (!ob->momentumz) return NO_MOVEMENT; } } else { MWALLSTOP = true; if (!ob->momentumz) return NO_MOVEMENT; } } else { if (mw->flags & MW_ABOVEPASSABLE) { if (mw->flags & MW_MIDDLEPASSABLE) // ==> not bottom { if (ob->z > LOWFALLCLIPZ+MAXSTEPHEIGHT) MWALLSTOP = true; else if (tryz >= LOWFALLCLIPZ) ClipHeight(ob,LOWFALLCLIPZ); } else if (mw->flags & MW_BOTTOMPASSABLE) { if ((ob->z > HIGHFALLCLIPZ+MAXSTEPHEIGHT) && (ob->z < LOWRISECLIPZ)) MWALLSTOP = true; else if (ob->z <= HIGHFALLCLIPZ+MAXSTEPHEIGHT) { if (tryz >= HIGHFALLCLIPZ) ClipHeight(ob,HIGHFALLCLIPZ); } else if (tryz <= LOWRISECLIPZ) ob->momentumz = 0; } else // ==> above only { if (ob->z > HIGHFALLCLIPZ+MAXSTEPHEIGHT) MWALLSTOP = true; else if (tryz >= HIGHFALLCLIPZ) ClipHeight(ob,HIGHFALLCLIPZ); } } else if (mw->flags & MW_MIDDLEPASSABLE) { if (mw->flags & MW_BOTTOMPASSABLE) //==> not above passable { if (ob->z >= HIGHRISECLIPZ) { if (tryz <= HIGHRISECLIPZ) ob->momentumz = 0; } else if (tryz <= HIGHRISECLIPZ) MWALLSTOP = true; } else //==> middle only { if (ob->z > LOWFALLCLIPZ+MAXSTEPHEIGHT) MWALLSTOP = true; else if (tryz >= LOWFALLCLIPZ) ClipHeight(ob,LOWFALLCLIPZ) else { if (ob->z >= HIGHRISECLIPZ) { if (tryz <= HIGHRISECLIPZ) ob->momentumz = 0; } else if (tryz <= HIGHRISECLIPZ) MWALLSTOP = true; } } } else // ==> bottompassable only { if (ob->z < LOWRISECLIPZ) MWALLSTOP = true; else if (tryz < LOWRISECLIPZ) ob->momentumz = 0; } } } else { if ( (mw->flags&MW_SHOOTABLE) && (mw->flags&MW_BLOCKINGCHANGES) && (ob->z >= nominalheight) ) { int speed=FindDistance(ob->momentumx,ob->momentumy); if ((speed>0x2800) && (!(ob->flags & FL_DYING))) { if (ob->obclass == playerobj) { DamageThing(ob,10); Collision(ob,(objtype*)mw,0,0); } UpdateMaskedWall(wall&0x3ff); if (tryz < nominalheight) ob->momentumz = 0; } else { MWALLSTOP = true; if (!ob->momentumz) return NO_MOVEMENT; } } else { MWALLSTOP = true; if (!ob->momentumz) return NO_MOVEMENT; } } } if (MWALLSTOP == true) return Z_MOVEMENT_ONLY; return OK_TO_CONTINUE; } movement_status CheckDoors(objtype *ob,int tryx,int tryy,int tryz) { int trytilex,trytiley; int ocl; trytilex = (tryx >> TILESHIFT); trytiley = (tryy >> TILESHIFT); ocl = ob->obclass; if (M_ISDOOR(trytilex,trytiley)) { doorobj_t*tempdoor; int doorn; doorn = tilemap[trytilex][trytiley]; tempdoor = doorobjlist[doorn&0x3ff]; if (tempdoor->action == dr_open) { if (ob->z >= nominalheight) { if (tryz < nominalheight) ob->momentumz = 0; return OK_TO_CONTINUE; } } if (ocl == inertobj) { CheckSpecialGibMovement(tempdoor); } else if ((ocl == playerobj) || (ocl > b_darksnakeobj)) return NO_MOVEMENT; else if (ob->state->think != T_Collide) { #define DOOR_LOCKED(door) \ (((door->flags & DF_ELEVLOCKED) || (door->lock)) && \ (ob->obclass != b_darianobj) \ ) #define GAS_DOOR(x,y) (MISCVARS->GASON && (MAPSPOT(x,y,1) == GASVALUE)) if ((!DOOR_LOCKED(tempdoor)) && (!GAS_DOOR(trytilex,trytiley)) ) //) { ob->door_to_open = doorn&0x3ff; LinkedOpenDoor(ob->door_to_open); if (tempdoor->eindex != -1) OperateElevatorDoor(doorn&0x3ff); } //if ((nstate = M_S(USE)) != NULL) //{ob->whatever = ob->state; // NewState(ob,nstate); // ob->flags |= FL_USE; // } return NO_MOVEMENT; } else return NO_MOVEMENT; } return OK_TO_CONTINUE; } boolean ActorTryMove(objtype*ob,int tryx, int tryy, int tryz) { movement_status (*reduced_movement_check[3])(objtype*,int,int,int)= { CheckRegularWalls, CheckMaskedWalls, CheckDoors, }; movement_status (*complete_movement_check[5])(objtype*,int,int,int)= { CheckOtherActors, CheckRegularWalls, CheckStaticObjects, CheckMaskedWalls, CheckDoors, }; movement_status (**movement_function)(objtype*,int,int,int); movement_status movement_check_result; int numcheckfunctions; int i; boolean xyblocked; if ((tryz < -30) && (sky==0) && (ob->obclass != inertobj)) { ob->z = -28; ob->momentumz = 0; return false; } if ((!InMapBounds(tryx>>16,tryy>>16)) || ((ob->obclass != playerobj) && (IsWindow((tryx>>16),(tryy>>16)))) ) return false; switch(ob->obclass) { case inertobj: case bladeobj: case firejetobj: movement_function = &reduced_movement_check[0]; numcheckfunctions = 3; break; default: movement_function = &complete_movement_check[0]; numcheckfunctions = 5; break; } for(xyblocked=false,i=0; i<numcheckfunctions; i++) { movement_check_result = movement_function[i](ob,tryx,tryy,tryz); if (movement_check_result == Z_MOVEMENT_ONLY) xyblocked = true; else if (movement_check_result == NO_MOVEMENT) return false; } if (xyblocked == true) return false; return true; } void PushWallMove(int num) { int tcl; pwallobj_t *pwall; int dx,dy; int actrad; objtype *temp; boolean pushem; int tryx,tryy,areanumber,trytilex,trytiley; pwall=pwallobjlist[num]; actrad = PWALLRAD + 0x5000; tryx = (pwall->x + pwall->momentumx); tryy = (pwall->y + pwall->momentumy); trytilex = (tryx >> 16); trytiley = (tryy >> 16); areanumber = AREANUMBER(trytilex,trytiley); for(temp=firstareaactor[areanumber]; temp; temp=temp->nextinarea) { tcl = temp->obclass; if (temp->flags & FL_HEAD) //ignore NME's head and wheels continue; if ((temp->flags & FL_DYING) || (!(temp->flags & FL_SHOOTABLE))) continue; if (tcl > b_darianobj) continue; dx = abs(tryx - temp->x); if (dx > actrad) continue; dy = abs(tryy - temp->y); if (dy > actrad) continue; if (pwall->flags&PW_DAMAGE) { if (!((tcl == playerobj) && (temp->flags & FL_AV))) DamageThing(temp,5); Collision(temp,(objtype*)pwall,0,0); M_CheckPlayerKilled(temp); if (temp->flags & FL_DYING) return; } pushem=false; switch (pwall->dir) { #define PWALLTOL (0xc000) case north: if ((temp->y<pwall->y) && (dx<PWALLTOL)) pushem=true; break; case east: if ((temp->x>pwall->x) && (dy<PWALLTOL)) pushem=true; break; case northeast: if ((temp->y<pwall->y) && (dx<PWALLTOL)) pushem=true; else if ((temp->x>pwall->x) && (dy<PWALLTOL)) pushem=true; break; case northwest: if ((temp->y<pwall->y) && (dx<PWALLTOL)) pushem=true; else if ((temp->x<pwall->x) && (dy<PWALLTOL)) pushem=true; break; case south: if ((temp->y>pwall->y) && (dx<PWALLTOL)) pushem=true; break; case west: if ((temp->x<pwall->x) && (dy<PWALLTOL)) pushem=true; break; case southeast: if ((temp->y>pwall->y) && (dx<PWALLTOL)) pushem=true; else if ((temp->x>pwall->x) && (dy<PWALLTOL)) pushem=true; break; case southwest: if ((temp->y>pwall->y) && (dx<PWALLTOL)) pushem=true; else if ((temp->x<pwall->x) && (dy<PWALLTOL)) pushem=true; break; default: //Error ("Pushwall #%d has an illegal direction %d \n",num,pwall->dir); break; } //if (!pushem) //continue; //temp->momentumx = temp->momentumy = 0; if (temp->obclass==playerobj) temp->flags|=FL_PUSHED; if (!pushem) { Collision(temp,(objtype*)pwall,-temp->momentumx,-temp->momentumy); continue; } if ((temp->obclass >= lowguardobj) && (temp->obclass < roboguardobj)) { temp->momentumx = temp->momentumy = temp->momentumz = 0; temp->hitpoints = 0; if (gamestate.violence >= vl_high) temp->flags |= FL_HBM; Collision(temp,(objtype*)pwall,0,0); /* if (gamestate.violence < vl_high) {if ((tstate = UPDATE_STATES[CRUSH][temp->obclass - lowguardobj])!=NULL) NewState(temp,tstate); else Error("\n\Null low-violence crush state in push wall crush, instance of %s",debugstr[temp->obclass]); } else {temp->shapeoffset = 0; //tactor->flags|=FL_HBM; NewState(temp,&s_guts1); //KillActor(temp); }*/ SD_PlaySoundRTP(SD_ACTORSQUISHSND,temp->x,temp->y); } else { if (!ActorTryMove(temp,temp->x + temp->momentumx,temp->y + temp->momentumy, temp->z + (temp->momentumz >> 16)) ) { DamageThing(temp,30); if ((temp->obclass==playerobj) && (temp->hitpoints <= 0)) temp->target = (objtype *)pwall; } Collision(temp,(objtype*)pwall,pwall->momentumx-temp->momentumx,pwall->momentumy-temp->momentumy); M_CheckPlayerKilled(temp); } } } void ActorMovement (objtype *ob) { int tryx,tryy,tryz,limitok,max,friction,ocl; if ((ob->obclass == strikeguardobj) && (!(ob->flags & FL_DYING)) && (gamestate.difficulty > gd_easy) ) { AvoidPlayerMissile(ob); ob->flags &= ~FL_FULLLIGHT; } if ((!ob->momentumx) && (!ob->momentumy) && (!ob->momentumz)) { if (ob->flags & FL_RIDING) goto ride; else return; } limitok = 1; friction = ACTORFRICTION; if (!(ob->flags & FL_DYING)) friction >>= 1; ocl = ob->obclass; if (ocl == playerobj) { playertype *pstate; M_LINKSTATE(ob,pstate); max = pstate->topspeed; friction = PLAYERFRICTION; if ((ob->temp2 == PITFALL) || (ob->temp2 == PITRISE)) friction >>= 4; } else if (/*(ob->state->think != T_Collide) &&*/ (ocl != b_robobossobj) && (ocl != boulderobj) && (ocl !=b_darkmonkobj) && (ocl != b_darksnakeobj) && (ocl != inertobj) && (ocl != collectorobj)) max = MAXMOVE; else limitok = 0; if (limitok) { if (ocl == playerobj) { int dist,scale; dist = FindDistance(ob->momentumx,ob->momentumy); if (dist > max) { scale = FixedDiv2(max,dist); ob->momentumx = FixedMul(ob->momentumx,scale); ob->momentumy = FixedMul(ob->momentumy,scale); } } else { if (ob->momentumx > max) ob->momentumx = max; else if (ob->momentumx < -max) ob->momentumx = -max; if (ob->momentumy > max) ob->momentumy = max; else if (ob->momentumy < -max) ob->momentumy = -max; } } tryx = ob->x + ob->momentumx; tryy = ob->y + ob->momentumy; tryz = ob->z + (ob->momentumz >> 16); if (ocl != playerobj) ob->flags &= ~FL_STUCK; if (!ActorTryMove (ob, tryx, tryy, tryz)) { if (ocl == playerobj) { if (!(ob->flags & FL_ELASTO)) PlayerSlideMove (ob); else { if (ActorTryMove(ob,tryx, ob->y-ob->momentumy,tryz)) ob->momentumy = -(ob->momentumy); else if (ActorTryMove(ob,ob->x-ob->momentumx,tryy,tryz)) ob->momentumx = -(ob->momentumx); else ZEROMOM; } } else { ZEROMOM; ob->flags |= FL_STUCK; return; } } MoveActor(ob); ride: if (ob->flags & FL_RIDING) { objtype *ride = (objtype*)(ob->whatever); ob->z += (ride->momentumz >> 16); if ((ride->momentumx || ride->momentumy) && ActorTryMove(ob,ob->x+ride->momentumx,ob->y+ride->momentumy,tryz) ) SetFinePosition(ob,ob->x+ride->momentumx,ob->y+ride->momentumy); } #define SLIDER(ob) ((ob->flags & FL_NOFRICTION) && (ob->state->think != T_Collide)) #define AIRBORNE(ob) ((ob->obclass != playerobj) && (ob->z != nominalheight) &&\ (!IsPlatform(ob->tilex,ob->tiley)) && \ (DiskAt(ob->tilex,ob->tiley) == NULL) \ ) if (SLIDER(ob) || AIRBORNE(ob)) return; if ( (abs(ob->momentumx) < STOPSPEED) && (abs(ob->momentumy) < STOPSPEED) ) { ZEROMOM; } else if ((ob->flags & FL_DYING) && (ob->state == ob->state->next)) { ob->momentumx = FixedMul (ob->momentumx, DEADFRICTION); ob->momentumy = FixedMul (ob->momentumy, DEADFRICTION); } else { ob->momentumx = FixedMul (ob->momentumx, friction); ob->momentumy = FixedMul (ob->momentumy, friction); } } void T_Guts(objtype*ob) { if (ob->ticcount) return; SpawnParticles(ob,GUTS,50); } void T_Special(objtype*ob) { if (ob->ticcount) return; #if (SHAREWARE == 0) if (ob->state == &s_NMEheadexplosion) { ob->z -= 42; SetGibSpeed(0x4000); SpawnParticles(ob,gt_sparks,100); ResetGibSpeed(); SD_PlaySoundRTP(SD_EXPLODESND,ob->x,ob->y); return; } #endif if (ob->obclass != b_robobossobj) return; NewState(ob,&s_bossdeath); } void SpawnBoulder(int tilex,int tiley,int dir) { #if (SHAREWARE == 1) tilex = tilex; tiley = tiley; dir = dir; Error("Boulders aren't allowed in shareware!"); #endif #if (SHAREWARE == 0) SpawnNewObj(tilex,tiley,&s_boulderspawn,inertobj); new->z = 0; PreCacheActor(boulderobj,0); new->dir = 2*dir; #endif } #define InitSprayPart(newflags) \ { \ new->hitpoints = starthitpoints[gamestate.difficulty][b_robobossobj]; \ new->dir = dir*4; \ new->speed = 7*SPDPATROL; \ new->door_to_open = -1; \ new->flags |= (newflags); \ } \ void SpawnMultiSpriteActor(classtype actorclass, int tilex,int tiley,int dir) { #if (SHAREWARE==1) actorclass = actorclass; tilex = tilex; tiley = tiley; dir = dir; Error("\nSPRAY not allowed in shareware !"); #else { objtype *temp; gamestate.killtotal++; SpawnNewObj(tilex,tiley,&s_NMEstand,actorclass); InitSprayPart(FL_BLOCK|FL_NOFRICTION|FL_SHOOTABLE); new->temp1 = -1; // temp1 used as one-event queue for directions when chasing // -1 when isn't waiting to try new dir, dirnumber when waiting temp = new; SpawnNewObj(tilex,tiley,&s_NMEhead1,actorclass); InitSprayPart(FL_NOFRICTION|FL_SHOOTABLE|FL_HEAD|FL_NEVERMARK); //new->whatever = temp; // head points to body temp->whatever = new; // body points to head SpawnNewObj(tilex,tiley,&s_NMEwheels2,actorclass); InitSprayPart(FL_NOFRICTION|FL_SHOOTABLE|FL_HEAD|FL_NEVERMARK); //new->whatever = temp; // head points to body temp->target = new; // body also points to wheels actorat[tilex][tiley] = NULL; PreCacheActor(b_robobossobj,0); } #endif } void SpawnSnake(int tilex,int tiley) { #if (SHAREWARE == 1) tilex = tilex; tiley = tiley; Error("snake not allowed in shareware!"); #else GetNewActor(); MakeActive(new); new->flags |= (FL_DONE|FL_ABP|FL_NEVERMARK); SetTilePosition(new,tilex,tiley); SetVisiblePosition(new,new->x,new->y); new->obclass = b_darkmonkobj; new->which = ACTOR; new->z = nominalheight; if (SNAKELEVEL == 2) NewState(new,&s_darkmonkfastspawn); else NewState(new,&s_darkmonkhspawn); #endif } void SpawnGunThingy(classtype which, int tilex, int tiley, int dir) { #if (SHAREWARE == 1) which = which; tilex = tilex; tiley = tiley; dir = dir; Error("no emplacements allowed in shareware!"); #else SpawnNewObj(tilex,tiley,&s_gunstand,which); if (!loadedgame) gamestate.killtotal++; PreCacheActor(patrolgunobj,0); new->hitpoints = starthitpoints[gamestate.difficulty][which]; new->dir = dir*2; // new->speed = 0x500; // ParseMomentum(new,dirangle8[new->dir]); new->flags |= (FL_BLOCK|FL_SHOOTABLE); #endif } void SpawnFourWayGun(int tilex, int tiley) { #if (SHAREWARE == 1) tilex = tilex; tiley = tiley; Error("no 4-way emplacements allowed in shareware!"); #else SpawnNewObj(tilex,tiley,&s_4waygun,patrolgunobj); if (!loadedgame) gamestate.killtotal++; PreCacheActor(patrolgunobj,0); new->temp1 = -1; new->hitpoints = starthitpoints[gamestate.difficulty][patrolgunobj]*3; new->flags |= (FL_BLOCK|FL_SHOOTABLE); #endif } /* ======================================================================= = = NON-SHAREWARE CODE = ======================================================================= */ #if (SHAREWARE == 0) void T_BoulderSpawn(objtype*ob) { objtype *tactor; int dx,dy,cl; if (!(ob->flags & FL_ACTIVE)) return; else if (!ob->ticcount) { for(tactor = firstareaactor[ob->areanumber]; tactor; tactor = tactor->nextinarea) { cl = tactor->obclass; if (tactor == ob) continue; if (!(tactor->flags & FL_SHOOTABLE)) continue; dx = abs(tactor->x - ob->x); if (dx > MINACTORDIST) continue; dy = abs(tactor->y - ob->y); if (dy > MINACTORDIST) continue; if ((cl == b_heinrichobj) || (cl== b_darkmonkobj) || (cl == b_darianobj) || (cl == b_robobossobj) || (cl == pillarobj) || (cl == wallopobj) || (cl == boulderobj)) return; else break; } SpawnNewObj(ob->tilex,ob->tiley,&s_boulderdrop1,boulderobj); new->z = 0; new->dir = ob->dir; //new->angle = dirangle8[new->dir]; new->speed = 0x4000; ParseMomentum(new,dirangle8[new->dir]); new->flags |= (FL_BLOCK|FL_NOFRICTION); new->flags &= ~FL_SHOOTABLE; new->whatever = ob; if (tactor) new->target = tactor; MakeActive(new); new->flags |= FL_ABP; } } void T_BoulderDrop(objtype*ob) { int dx,dy,dz; objtype * tactor; statetype *tstate; if (ob->state == &s_boulderdrop12) { if (ob->z == nominalheight) NewState(ob,&s_boulderroll1); else if (ob->momentumz) { ob->z += (ob->momentumz>>16); ob->momentumz += (GRAVITY<<1); if (ob->z > nominalheight) { ob->z = nominalheight; ob->momentumz = 0; //ob->flags &= ~FL_NOFRICTION; } } else if (!ob->temp1) { ob->momentumz = (GRAVITY<<6); ob->temp1 = 1; } } if (ob->ticcount) return; if (ob->state->condition & SF_SOUND) SD_PlaySoundRTP(SD_BOULDERFALLSND,ob->x,ob->y); tactor = (objtype*)(ob->target); if (tactor && (!(tactor->flags & FL_DYING))) { dx = tactor->x - ob->x; dy = tactor->y - ob->y; dz = tactor->z - ob->z; if ((abs(dx) < MINACTORDIST) && (abs(dy) < MINACTORDIST) && (abs(dz) < 50)) { if (tactor->obclass != playerobj) { tactor->momentumx = tactor->momentumy = tactor->momentumz = 0; tactor->flags |= FL_DYING; tactor->hitpoints = 0; if (gamestate.violence < vl_high) { if ((tstate = UPDATE_STATES[CRUSH][tactor->obclass - lowguardobj])!=NULL) NewState(tactor,tstate); //else //Error("\n\Null low-violence crush state in boulder drop, instance of %s",debugstr[tactor->obclass]); } else { tactor->shapeoffset = 0; //tactor->flags|=FL_HBM; NewState(tactor,&s_guts1); } } else { DamageThing(tactor,200); Collision(tactor,ob,0,0); M_CheckPlayerKilled(tactor); } SD_PlaySoundRTP(SD_ACTORSQUISHSND,tactor->x,tactor->y); ob->target = NULL; } } } void CheckCrush(objtype*ob) { objtype *temp; int dx,dy,dz; for(temp = PLAYER[0]; temp != PLAYER[numplayers-1]->next; temp=temp->next) { if (ob->flags & FL_DYING) continue; dx = abs(temp->x - ob->x); if (dx > MINACTORDIST) continue; dy = abs(temp->y - ob->y); if (dy > MINACTORDIST) continue; dz = abs(temp->z - ob->z); if (dz > (MINACTORDIST>>10)) continue; if (!ob->ticcount) DamageThing(temp,EnvironmentDamage(ob)); Collision(temp,ob,ob->momentumx-temp->momentumx,ob->momentumy-temp->momentumy); M_CheckPlayerKilled(temp); } } void T_BoulderMove(objtype*ob) { if (MAPSPOT(ob->tilex,ob->tiley,1) == 395) { NewState(ob,&s_bouldersink1); return; } if (NOMOM) ParseMomentum(ob,dirangle8[ob->dir]); if ((!ob->ticcount) && (ob->state->condition & SF_SOUND) && areabyplayer[ob->areanumber]) SD_PlaySoundRTP(BAS[ob->obclass].operate,ob->x,ob->y); SelectPathDir(ob); } /* ========================================================================= = = Boss Functions = ========================================================================= */ //***************************** Esau ************************************ enum { ESAU_USING_HOLES=1, ESAU_LEAVING_CONTROL_ROOM, ESAU_USING_TOUCH_PEDASTALS, ESAU_CHASING_PLAYER }; void T_EsauWait(objtype*ob) { int dist; dist = FindDistance(ob->tilex-PLAYER[0]->tilex,ob->tiley-PLAYER[0]->tiley); MISCVARS->ESAU_SHOOTING = false; if (ob->dirchoosetime) ob->dirchoosetime --; if ((dist>81) || (dist<36)) { if (CheckLine(ob,PLAYER[0],MISSILE)) { NewState(ob,&s_darianshoot1); ob->momentumx = ob->momentumy = 0; } return; } else if ((!ob->dirchoosetime) && (CheckLine(ob,PLAYER[0],SHOOT))) { NewState(ob,&s_dariandefend1); ob->dirchoosetime = (GameRandomNumber("T_EsauWait",0) % 35) + 17;//35; return; } } void T_EsauRise(objtype*ob) { int newarea,oldarea; // if (gamestate.victoryflag) // return; if (!ob->ticcount) { //Debug("\n tx before: %d, ty before: %d", // ob->targettilex,ob->targettiley); SelectTouchDir(ob); if (ob->targettilex || ob->targettiley) { //Debug("\n ob->tilex: %d, ob->tiley: %d, targettilex: %d, targettiley: %d", // ob->tilex, ob->tiley, ob->targettilex, ob->targettiley); SetTilePosition(ob,ob->targettilex,ob->targettiley); SetVisiblePosition(ob,ob->x,ob->y); oldarea = ob->areanumber; newarea = AREANUMBER(ob->tilex,ob->tiley); if (oldarea != newarea) { RemoveFromArea(ob); ob->areanumber = newarea; MakeLastInArea(ob); } } else MISCVARS->EPOP[ob->temp3].x = MISCVARS->EPOP[ob->temp3].y = 0; ob->dirchoosetime= (GameRandomNumber("T_EsauRise",0) % 35) + 17; MISCVARS->ESAU_HIDING = false; MISCVARS->ESAU_SHOOTING = true; ob->flags |= FL_SHOOTABLE; } } void T_EsauChase(objtype*ob) { int dx,dy,chance,dist; statetype *temp; if ((ob->tilex == ob->targettilex) && (ob->tiley == ob->targettiley)) { if (MISCVARS->DSTATE == ESAU_USING_HOLES) { MISCVARS->ESAU_HIDING = true; MISCVARS->ESAU_SHOOTING = false; SD_PlaySoundRTP(SD_DARIANHIDESND,ob->x,ob->y); NewState(ob,&s_dariansink1); ob->flags &= ~FL_SHOOTABLE; return; } else if (MISCVARS->DSTATE == ESAU_LEAVING_CONTROL_ROOM) { if (!MISCVARS->doorcount) { SetTilePosition(ob,ob->tilex,ob->tiley); SetVisiblePosition(ob,ob->x,ob->y); } MISCVARS->doorcount ++; if (MISCVARS->doorcount == 4) MISCVARS->DSTATE = ESAU_USING_HOLES; else // hack to FORCE esau to walk through door { switch (ob->temp1) { case east: ob->targettilex ++; break; case west: ob->targettilex --; break; case north: ob->targettiley --; break; case south: ob->targettiley ++; break; } } SelectTouchDir(ob); return; } } if (touchsprite && (touchsprite->itemnumber == stats[stat_dariantouch].type)) { dx = touchsprite->x - ob->x; dy = touchsprite->y - ob->y; if (((dx > -0x5000) && (dx < 0x5000)) && ((dy > -0x5000) && (dy < 0x5000))) { SD_PlaySoundRTP(SD_DARIANGONNAUSESND,ob->x,ob->y); NewState(ob,&s_darianuse1); return; } } if (ob->dirchoosetime) ob->dirchoosetime --; if (NOMOM || (!ob->dirchoosetime)) { SelectTouchDir(ob); ob->dirchoosetime = M_CHOOSETIME(ob); } else ActorMovement(ob); if (!ob->ticcount) { if (CheckLine(ob,PLAYER[0],MISSILE)) // got a shot at player? { if (Near(ob,PLAYER[0],1)) chance = 300; else { dx = abs(PLAYER[0]->tilex-ob->tilex); dy = abs(PLAYER[0]->tiley-ob->tiley); dist = (dx>dy)?dx:dy; chance = 400/dist; } if (GameRandomNumber("T_EsauChase",0) <chance) { if ((temp=M_S(AIM)) != NULL) { NewState(ob,temp); ob->dirchoosetime = 0; ob->momentumx = ob->momentumy = 0; SetVisiblePosition(ob,ob->x,ob->y); return; } } if (MISCVARS->ESAU_SHOOTING) { SetVisiblePosition(ob,ob->x,ob->y); return; } } } } void T_EsauSpears(objtype*ob) { if (ob->ticcount == (ob->state->tictime>>1)-1) { OLDTILEX = PLAYER[0]->tilex; OLDTILEY = PLAYER[0]->tiley; } else if (!ob->ticcount) { SpawnNewObj(OLDTILEX,OLDTILEY,&s_speardown1,spearobj); new->flags |= FL_ABP; MakeActive(new); } } void FindDoor(objtype*ob) { int i,area1,area2,min,curr, dest1x,dest1y,dest2x,dest2y, d1,d2; dirtype tdir1,tdir2; doorobj_t*dr; min = 0x7fffffff; for(i=0; i<doornum; i++) { dr = doorobjlist[i]; if (dr->vertical) { area1 = MAPSPOT(dr->tilex-1,dr->tiley,0)-AREATILE; dest1x = dr->tilex-1; dest1y = dr->tiley; tdir1 = east; area2 = MAPSPOT(dr->tilex+1,dr->tiley,0)-AREATILE; dest2x = dr->tilex+1; dest2y = dr->tiley; tdir2 = west; } else { area1 = MAPSPOT(dr->tilex,dr->tiley-1,0)-AREATILE; dest1x = dr->tilex; dest1y = dr->tiley-1; tdir1 = south; area2 = MAPSPOT(dr->tilex,dr->tiley+1,0)-AREATILE; dest2x = dr->tilex; dest2y = dr->tiley+1; tdir2 = north; } //============================================================ #define CheckMinDist(destx,desty,dir) \ { \ curr = FindDistance(destx-ob->tilex,desty-ob->tiley); \ if (curr < min) \ { \ min = curr; \ ob->targettilex = destx; \ ob->targettiley = desty; \ ob->temp1 = dir; \ } \ } //============================================================ if (area1 == ob->areanumber) { if (area1 == area2) { d1 = FindDistance(dest1x-ob->tilex,dest1y-ob->tiley); d2 = FindDistance(dest2x-ob->tilex,dest2y-ob->tiley); if (d2 < d1) //swap areas { CheckMinDist(dest2x,dest2y,tdir2); continue; } } CheckMinDist(dest1x,dest1y,tdir1); } else if (area2 == ob->areanumber) CheckMinDist(dest2x,dest2y,tdir2); } } int FindTouch(objtype *ob) { int i,curr,min,tx,ty,noneleft; statobj_t* tempstat; min = 0x7fffffff; noneleft = 1; for(i=0; i<MISCVARS->nexttouch; i++) { if (MISCVARS->ETOUCH[i].x || MISCVARS->ETOUCH[i].y) { noneleft = 0; tx = MISCVARS->ETOUCH[i].x; ty = MISCVARS->ETOUCH[i].y; tempstat = sprites[tx][ty]; curr = FindDistance(tx-ob->tilex,ty-ob->tiley); if (curr < min) { min = curr; ob->targettilex = tx; ob->targettiley = ty; touchsprite = tempstat; } } } return (!noneleft); } typedef enum { down_in_a_hole=-1, no_holes_available=0, holes_unreachable=1, hole_targetted=2 } hiding_status; hiding_status HoleStatus(objtype*ob) { int i,tx,ty,dist,noneleft,invisible,curr,min; tpoint dummy,*dptr = &dummy; objtype *tactor; _2Dpoint *tdptr; min = 0x7fffffff; noneleft = 1; for(i=0; i<MISCVARS->nextpop; i++) { tdptr = &(MISCVARS->EPOP[i]); if (tdptr->x || tdptr->y) { tactor = (objtype*)actorat[tdptr->x][tdptr->y]; if (tactor && (tactor->obclass == pillarobj)) { tdptr->x = 0; tdptr->y = 0; MISCVARS->popsleft --; } } } if (MISCVARS->popsleft > 1) { for(i=0; i<MISCVARS->nextpop; i++) { tdptr = &(MISCVARS->EPOP[i]); if (tdptr->x || tdptr->y) { tx = tdptr->x; ty = tdptr->y; if ((PLAYER[0]->tilex == tx) || (PLAYER[0]->tiley == ty)) continue; if (MISCVARS->ESAU_HIDING) { dist = FindDistance(PLAYER[0]->tilex-tx,PLAYER[0]->tiley-ty); if ((ob->tilex == tx) && (ob->tiley == ty) && (MISCVARS->popsleft != 1)) continue; noneleft = 0; if ((MAPSPOT(tx,ty,0)-AREATILE) == ob->areanumber) { ob->targettilex = tx; ob->targettiley = ty; ob->temp3 = i; if ((dist < 81) && (dist > 36)) return down_in_a_hole; } } else if (!MISCVARS->ESAU_SHOOTING) { curr = FindDistance(tx-ob->tilex,ty-ob->tiley); if (curr < min) { min = curr; noneleft = 0; dptr->which = ACTOR; SetTilePosition(dptr,tx,ty); //dptr->x = (tx << TILESHIFT) + TILEGLOBAL/2; //dptr->y = (ty << TILESHIFT) + TILEGLOBAL/2; dptr->z = ob->z; invisible = 0; if ((!CheckLine(ob,dptr,SHOOT)) && (MISCVARS->DSTATE != ESAU_USING_HOLES)) { invisible = 1; MISCVARS->DSTATE = ESAU_LEAVING_CONTROL_ROOM; } else MISCVARS->DSTATE = ESAU_USING_HOLES; ob->targettilex = tx; ob->targettiley = ty; } } } } } if (MISCVARS->ESAU_HIDING) return down_in_a_hole; if (noneleft) { MISCVARS->DSTATE = ESAU_CHASING_PLAYER; return no_holes_available; } if (invisible) //leave present room return holes_unreachable; return hole_targetted; } void SelectTouchDir (objtype *ob) { int dx,dy; hiding_status hole; dirtype d[3]; dirtype tdir, olddir, turnaround; olddir=ob->dir; turnaround= opposite[olddir]; if (!MISCVARS->notouch) { if (!FindTouch(ob)) MISCVARS->notouch = 1; else MISCVARS->DSTATE = ESAU_USING_TOUCH_PEDASTALS; } else if ((!MISCVARS->noholes) && (MISCVARS->DSTATE != ESAU_LEAVING_CONTROL_ROOM)) { hole = HoleStatus(ob); switch(hole) { case down_in_a_hole: return; case no_holes_available: MISCVARS->noholes = 1; break; case holes_unreachable: FindDoor(ob); break; default: break; } } else if (MISCVARS->DSTATE == ESAU_CHASING_PLAYER) // only gets here if all gimmicks (touch tables, // holes) are inoperative { ob->flags |= FL_SHOOTABLE; ob->targettilex = PLAYER[0]->tilex; ob->targettiley = PLAYER[0]->tiley; } /* if (DSTATE == SDOOR) {dx = ((ob->targettilex<<16)+TILEGLOBAL/2) - ob->x; dy = ob->y - ((ob->targettiley<<16)+TILEGLOBAL/2); angle = atan2_appx(dx,dy); ZEROMOM; ParseMomentum(ob,angle); ActorMovement(ob); if (ob->momentumx || ob->momentumy) {ob->angle = angle; ob->dir = angletodir[ob->angle]; return; } } else */ dx = ob->targettilex - ob->tilex; dy = ob->tiley - ob->targettiley; d[1]=nodir; d[2]=nodir; if (dx>0) d[1]= east; else if (dx<0) d[1]= west; if (dy>0) d[2]=north; else if (dy<0) d[2]=south; if (GameRandomNumber("SelectTouchDir",0)<128) { tdir=d[1]; d[1]=d[2]; d[2]=tdir; } ZEROMOM; if (d[1]!=nodir) M_CHECKDIR(ob,d[1]); if (d[2]!=nodir) M_CHECKDIR(ob,d[2]); if (GameRandomNumber("SelectTouchDir",ob->obclass)>128) //randomly determine direction of search { for (tdir=north; tdir<=west; tdir++) { if (tdir!=turnaround) M_CHECKDIR(ob,tdir); } } else { for (tdir=west; tdir>=north; tdir--) { if (tdir!=turnaround) M_CHECKDIR(ob,tdir); } } if (turnaround != nodir) M_CHECKDIR(ob,turnaround); if (olddir!=nodir) M_CHECKDIR(ob,olddir); } //************** Krist **************************************************** void CheckRunover(objtype*ob) { int dx,dy,dz; dx = abs(PLAYER[0]->x - ob->x); if (dx > MINACTORDIST) return; dy = abs(PLAYER[0]->y - ob->y); if (dy > MINACTORDIST) return; dz = abs(PLAYER[0]->z - ob->z); if (dz > 10) return; locplayerstate->heightoffset = 18 + locplayerstate->playerheight; locplayerstate->oldheightoffset = locplayerstate->heightoffset; PLAYER[0]->temp2 = RENORMALIZE; DamageThing(PLAYER[0],30); Collision(PLAYER[0],ob,0,0); M_CheckPlayerKilled(PLAYER[0]); } void T_HeinrichChase(objtype*ob) { int dx,dy,dist,chance,perpangle; // statetype *temp; boolean doorok; CheckRunover(ob); // ob->flags &= ~FL_DODGE; if (CheckLine(ob,PLAYER[0],SIGHT)) { ob->targettilex = PLAYER[0]->x; ob->targettiley = PLAYER[0]->y; } if (!ob->ticcount) { // if (gamestate.victoryflag) // return; if (CheckLine(ob,PLAYER[0],SHOOT)) // got a shot at PLAYER[0]? { dx = abs(ob->tilex - PLAYER[0]->tilex); dy = abs(ob->tiley - PLAYER[0]->tiley); dist = dx>dy ? dx : dy; if (!dist || dist==1) chance = 300; else chance = 2400/dist; if (GameRandomNumber("T_HeinrichChase",0) <chance) { tpoint dummy,*dptr=&dummy; if (Near(ob,PLAYER[0],2)) goto cdoor; perpangle = AngleBetween(ob,PLAYER[0]) + ANGLES/4; Fix(perpangle); dptr->which = ACTOR; dptr->x = ob->x + FixedMul(0x10000l,costable[perpangle]); dptr->y = ob->y - FixedMul(0x10000l,sintable[perpangle]); dptr->z = ob->z; if (!CheckLine(dptr,PLAYER[0],SHOOT)) goto cdoor; ob->target = PLAYER[0]; NewState(ob,M_S(AIM)); ob->dirchoosetime = 0; return; } } } cdoor: doorok = NextToDoor(ob); if (ob->dirchoosetime) ob->dirchoosetime--; if ((ob->flags & FL_STUCK) || (!ob->dirchoosetime) || doorok) { /*if ((ob->flags & FL_DODGE) && (!doorok)) SelectKristDodgeDir (ob); else */ SD_PlaySoundRTP(SD_KRISTMOTORSND,ob->x,ob->y); SelectKristChaseDir(ob); ob->dirchoosetime = 4*M_CHOOSETIME(ob); } else { if (NOMOM) ParseMomentum(ob,dirangle8[ob->dir]); ActorMovement(ob); } } void T_Heinrich_Defend (objtype*ob) { CheckRunover(ob); if (ob->dirchoosetime) ob->dirchoosetime--; if (MISCVARS->HRAMMING) ParseMomentum(ob,dirangle8[ob->dir]); if ((ob->flags & FL_STUCK) || (!ob->dirchoosetime)) { if (MISCVARS->HRAMMING) { if (!Near(ob,PLAYER[0],3)) { NewState(ob,M_S(CHASE)); ob->dirchoosetime = 0; return; } SelectKristChaseDir(ob); } else if (MISCVARS->HMINING) { SelectMineDir(ob); if (!MISCVARS->HMINING) goto hchase; ob->dirchoosetime = 5;//10; return; } else hchase: NewState(ob,M_S(CHASE)); ob->dirchoosetime = 0; } else { if (NOMOM) ParseMomentum(ob,dirangle8[ob->dir]); ActorMovement(ob); } } void T_Heinrich_Out_of_Control(objtype*ob) { if (ob->dirchoosetime) ob->dirchoosetime --; else { if (!ob->temp1) { SetGibSpeed(0x4000); SpawnParticles(ob,RANDOM,120); ResetGibSpeed(); NewState(ob,&s_dexplosion1); SD_PlaySoundRTP(SD_EXPLODESND,ob->x,ob->y); } else { ob->dir = dirorder[ob->dir][PREV]; ob->angle = dirangle8[ob->dir]; if (ob->dir == (unsigned)ob->temp2) { if (ob->temp1 > 1) ob->temp1--; else { if (ob->temp3 == 7) { SpawnNewObj(ob->tilex,ob->tiley,&s_megaexplosions,inertobj); new->temp1 = 25; new->flags |= FL_ABP; MakeActive(new); SpawnNewObj(ob->tilex,ob->tiley,&s_superparticles,inertobj); new->flags |= FL_ABP; PARTICLE_GENERATOR = new; MakeActive(new); } if (ob->temp3) ob->temp3 --; else ob->temp1 --; } } if (ob->temp1) ob->dirchoosetime = ob->temp1; else { ob->dirchoosetime = 70; // end of spin wait for megaexplosion if (PARTICLE_GENERATOR) { NewState(PARTICLE_GENERATOR,&s_megaremove); PARTICLE_GENERATOR = NULL; } } } } } void SelectKristChaseDir(objtype*ob) { int dx,dy,tx,ty,angle; dirtype dtry1,dtry2,tdir,olddir,next,prev,straight; //tpoint dummy,*dptr=&dummy; olddir=ob->dir; //dptr->which = ACTOR; //dptr->z = ob->z; if (ob->targettilex || ob->targettiley) { tx = ob->targettilex; ty = ob->targettiley; dx= tx - ob->x; dy= ob->y - ty; // SetFinePosition(dptr,tx,ty); if ( ((dx < 0x20000) && (dx > -0x20000)) && ((dy < 0x20000) && (dy > -0x20000))) { dx= PLAYER[0]->x-ob->x; dy= ob->y-PLAYER[0]->y; // SetFinePosition(dptr,PLAYER[0]->x,PLAYER[0]->y); } } else { dx= PLAYER[0]->x-ob->x; dy= ob->y-PLAYER[0]->y; //SetFinePosition(dptr,PLAYER[0]->x,PLAYER[0]->y); } angle = atan2_appx(dx,dy); straight = angletodir[angle]; /* if (ob->areanumber == PLAYER[0]->areanumber) {//tpoint newpos1,newpos2; //dirtype leftdir; //int leftangle1,leftangle2; if (CheckLine(ob,&dummy,DIRCHECK)) {//Debug("\ntrying straight dir %d",straight); M_CHECKTURN(ob,straight); //Debug("\nstraight dir %d failed",straight); } //leftdir = dirorder[straight][PREV]; //leftangle1 = dirangle8[leftdir]; //newpos1.which = ACTOR; //rightangle = dirangle[dirorder[straight][NEXT]]; //newpos1.x = ob->x + FixedMul(0x10000,costable[leftangle1]); //newpos1.y = ob->y - FixedMul(0x10000,sintable[leftangle1]); //newpos1.z = ob->z; //leftangle2 = dirangle8[dirorder[leftdir][PREV]]; //newpos2.which = ACTOR; //rightangle = dirangle[dirorder[straight][NEXT]]; //newpos2.x = ob->x + FixedMul(0x10000,costable[leftangle2]); //newpos2.y = ob->y - FixedMul(0x10000,sintable[leftangle2]); //newpos2.z = ob->z; //if (CheckLine(&newpos1,&dummy,SHOOT))// || CheckLine(&newpos2,&dummy,SHOOT)) {for(tdir = dirorder[straight][PREV];tdir != dirorder[straight][NEXT];tdir = dirorder[tdir][PREV]) {//Debug("\ntried left-hand rule dir %d",tdir); M_CHECKTURN(ob,tdir); } } //else //{for(tdir = dirorder[straight][NEXT];tdir != dirorder[straight][PREV];tdir = dirorder[tdir][NEXT]) // {//Debug("\ntrying right-hand rule dir %d",tdir); // M_CHECKTURN(ob,tdir); //Debug("\nright-hand rule dir %d failed\n",tdir); // } // } } else*/ { dtry1=nodir; dtry2=nodir; if (dx> ACTORSIZE) dtry1= east; else if (dx< -ACTORSIZE) dtry1= west; if (dy> ACTORSIZE) dtry2=north; else if (dy < -ACTORSIZE) dtry2= south; if (abs(dy)>abs(dx)) { tdir=dtry1; dtry1=dtry2; dtry2=tdir; } // ZEROMOM; ob->momentumx = FixedMul (ob->momentumx, DEADFRICTION>>gamestate.difficulty); ob->momentumy = FixedMul (ob->momentumy, DEADFRICTION>>gamestate.difficulty); M_CHECKTURN(ob,straight); if (dtry1 != nodir) M_CHECKTURN(ob,dtry1); if (dtry2 != nodir) M_CHECKTURN(ob,dtry2); if (dtry1 != nodir) { M_CHECKTURN(ob,dirorder[dtry1][NEXT]); M_CHECKTURN(ob,dirorder[dtry1][PREV]); } for(tdir = dirorder[olddir][NEXT]; tdir != olddir; tdir = dirorder[tdir][NEXT]) M_CHECKTURN(ob,tdir); ob->dir = olddir; } } void T_KristLeft(objtype*ob) { CheckRunover(ob); ActorMovement(ob); if (!ob->ticcount) { SD_PlaySoundRTP(SD_KRISTTURNSND,ob->x,ob->y); if (ob->dir != (unsigned)ob->temp1) ob->dir = dirorder[ob->dir][NEXT]; else { ob->temp1 = 0; NewState(ob,&s_heinrichchase); } } } void T_KristRight(objtype*ob) { CheckRunover(ob); ActorMovement(ob); if (!ob->ticcount) { SD_PlaySoundRTP(SD_KRISTTURNSND,ob->x,ob->y); if (ob->dir != (unsigned)ob->temp1) ob->dir = dirorder[ob->dir][PREV]; else { ob->temp1 = 0; NewState(ob,&s_heinrichchase); } } } void T_KristCheckFire(objtype*ob) { int perpangle,angle; tpoint dummy; if (!ob->ticcount) { angle = AngleBetween(ob,PLAYER[0]); if (ob->state == &s_heinrichshoot1) perpangle = angle + ANGLES/4; else perpangle = angle - ANGLES/4; Fix(perpangle); dummy.which = ACTOR; dummy.x = ob->x + FixedMul(0x4000,costable[angle]) + FixedMul(0x4000l,costable[perpangle]) + FixedMul(PROJSIZE,costable[perpangle]); // offset ahead plus // offset for left/right missile plus offset for missile // radius (will missile reach player without hitting wall,etc.) dummy.y = ob->y - FixedMul(0x4000,sintable[angle]) - FixedMul(0x4000l,sintable[perpangle]) - FixedMul(PROJSIZE,sintable[perpangle]); dummy.x -= (FixedMul(PROJSIZE,costable[perpangle])<<1); dummy.y += (FixedMul(PROJSIZE,sintable[perpangle])<<1); dummy.z = ob->z; if (!CheckLine(&dummy,PLAYER[0],SHOOT)) { NewState(ob,&s_heinrichchase); return; } } } void SelectMineDir(objtype*ob) { int angle,missangle; dirtype olddir,tdir,next,prev,destdir; static int nummines=0; if (!CheckLine(ob,PLAYER[0],SIGHT)) { NewState(ob,M_S(CHASE)); MISCVARS->HMINING = 0; return; } olddir = ob->dir; angle = AngleBetween(ob,PLAYER[0]); tdir = angletodir[angle]; destdir = opposite[tdir]; if (destdir != olddir) { next = dirorder[olddir][NEXT]; prev = dirorder[olddir][PREV]; if (dirdiff[destdir][next] < dirdiff[destdir][prev]) ob->dir = next; else ob->dir = prev; return; } nummines ++; missangle = angle; if (nummines == 2) missangle -= (ANGLES/36); else if (nummines == 3) missangle += (ANGLES/36); Fix(missangle); // if (missangle > (ANGLES - 1)) // missangle -= ANGLES; // else if (missangle < 0) // missangle += ANGLES; SpawnMissile(ob,h_mineobj,0x2000,missangle,&s_mine1,0xa000); new->dirchoosetime = 140; SD_PlaySoundRTP(SD_KRISTDROPSND,ob->x,ob->y); if (nummines == 3) { MISCVARS->HMINING = 0; nummines = 0; } } void A_HeinrichShoot(objtype* ob) { int angle,perpangle; if (!ob->ticcount) { angle = AngleBetween(ob,PLAYER[0]); if (ob->state == &s_heinrichshoot4) perpangle = angle + ANGLES/4; else perpangle = angle - ANGLES/4; Fix(perpangle); SpawnMissile(ob,missileobj,0x4000,angle,&s_missile1,0x8000); SD_PlaySoundRTP(BAS[ob->obclass].fire,ob->x,ob->y); SetFinePosition(new,new->x + FixedMul(0x4000l,costable[perpangle]), new->y - FixedMul(0x4000l,sintable[perpangle])); SetVisiblePosition(new,new->x,new->y); } } //***************************///////************************************** //***************************/ NME /************************************** //***************************///////************************************** void UpdateNMELinkedActors(objtype*ob) { objtype *head,*wheels; int oldarea; head = (objtype*)(ob->whatever); wheels = (objtype*)(ob->target); oldarea = head->areanumber; SetFinePosition(head,ob->x,ob->y); SetFinePosition(wheels,ob->x,ob->y); SetVisiblePosition(head,ob->x,ob->y); SetVisiblePosition(wheels,ob->x,ob->y); if (oldarea != ob->areanumber) { RemoveFromArea(head); head->areanumber = ob->areanumber; MakeLastInArea(head); RemoveFromArea(wheels); wheels->areanumber = ob->areanumber; MakeLastInArea(wheels); } } void T_OrobotChase(objtype*ob) { int dx,dy; if (CheckLine(ob,PLAYER[0],SIGHT)) { ob->targettilex = PLAYER[0]->tilex; ob->targettiley = PLAYER[0]->tiley; } if (!ob->ticcount) { if (NMEspincheck(ob)) return; dx = PLAYER[0]->x - ob->x; dy = ob->y - PLAYER[0]->y; /* if ((dx > -0x18000) && (dx < 0x18000) && (dy > -0x18000) && (dy < 0x18000)) {NewState(ob,&s_NMEavoid); return; } */ if (CheckLine(ob,PLAYER[0],SIGHT)) { int inrange; switch(gamestate.difficulty) { case gd_baby: inrange = Near(ob,PLAYER[0],6); break; case gd_easy: inrange = Near(ob,PLAYER[0],9); break; case gd_medium: inrange = Near(ob,PLAYER[0],12); break; case gd_hard: inrange = 1; break; } if ((!Near(ob,PLAYER[0],3)) && inrange) { SD_PlaySoundRTP(SD_NMEREADYSND,ob->x,ob->y); if ((ob->hitpoints < 2000) && (GameRandomNumber("NME special attack",0) < 120)) { int next,prev; next = dirorder16[ob->dir][NEXT]; prev = dirorder16[ob->dir][PREV]; ob->targettilex = (angletodir[atan2_appx(dx,dy)]<<1); if (dirdiff16[prev][ob->targettilex] < dirdiff16[next][ob->targettiley]) ob->temp3 = PREV; else ob->temp3 = NEXT; NewState(ob,&s_NMEspinfire); } else { NewState(ob,&s_NMEwindup); ob->temp3 = 0; } //NewState((objtype*)(ob->target),&s_NMEwheelspin); NewState((objtype*)(ob->target),&s_NMEwheels120); return; } } } if (ob->dirchoosetime) ob->dirchoosetime --; if ((ob->flags & FL_STUCK) || (!ob->dirchoosetime)) { SelectOrobotChaseDir(ob); ob->dirchoosetime = 4;//8; } else { ActorMovement(ob); UpdateNMELinkedActors(ob); } } void T_Saucer(objtype*ob) { int angle,dangle; if (!ob->ticcount) // if on track at end of each state, accelerate // towards PLAYER[0] { if (ob->state->condition & SF_SOUND) SD_PlaySoundRTP(SD_NMEREADYSND,ob->x,ob->y); angle = AngleBetween(ob,PLAYER[0]); dangle = ob->angle - angle; if ((dangle > -(ANGLES/72)) && (dangle < (ANGLES/72))) { if (ob->speed < 0x10000) { ob->speed += 0x200; ZEROMOM; ParseMomentum(ob,ob->angle); } } else // off track; zero mom. and select new dir. { ob->speed = 0x1000; ZEROMOM; ob->angle = angle; ParseMomentum(ob,ob->angle); } } MissileMovement(ob); } void T_NME_WindUp(objtype*ob) { objtype *head,*wheels; head = (objtype*)(ob->whatever); wheels = (objtype*)(ob->target); if (ob->dirchoosetime) { ob->dirchoosetime--; return; } ob->dirchoosetime = 0;//3; if (MISCVARS->NMErotate < 3) { head->dir = dirorder16[head->dir][NEXT]; MISCVARS->NMErotate ++; } else if (MISCVARS->NMErotate < 6) { head->dir = dirorder16[head->dir][PREV]; MISCVARS->NMErotate ++; } else if (MISCVARS->NMErotate < 9) { ob->dir = dirorder16[ob->dir][NEXT]; wheels->dir = ob->dir; MISCVARS->NMErotate++; } else if (MISCVARS->NMErotate < 12) { ob->dir = dirorder16[ob->dir][PREV]; wheels->dir = ob->dir; MISCVARS->NMErotate ++; } else { MISCVARS->NMErotate = 0; NewState(ob,&s_NMEattack); ob->dirchoosetime = 0; //ob->dirchoosetime = 50 - (ob->shapeoffset >> 2) - (gamestate.difficulty << 2);//70; if (!ob->temp2) NewState((objtype*)(ob->whatever),&s_NMEhead1rl); else NewState((objtype*)(ob->whatever),&s_NMEhead2rl); NewState(wheels,&s_NMEwheels2); } } #define SPRAYDIST 0x12000 void SelectOrobotChaseDir(objtype*ob) // this code is for head { int dx,dy,angle,tx,ty; int tdir,olddir,nextdir,prevdir; objtype* head,*wheels; head = (objtype*)(ob->whatever); wheels = (objtype*)(ob->target); olddir=head->dir; findplayer: if (ob->temp1 == -1) { if (ob->targettilex || ob->targettiley) { tx = (int)((ob->targettilex << TILESHIFT) + HALFGLOBAL1); ty = (int)((ob->targettiley << TILESHIFT) + HALFGLOBAL1); dx= tx - ob->x; dy= ob->y - ty; if (((dx <SPRAYDIST ) && (dx > -SPRAYDIST)) && ((dy <SPRAYDIST ) && (dy > -SPRAYDIST))) { dx= PLAYER[0]->x-ob->x; dy= ob->y - PLAYER[0]->y; } } else { dx= PLAYER[0]->x - ob->x; dy= ob->y - PLAYER[0]->y; } angle = atan2_appx(dx,dy); tdir = (((angletodir[angle])<<1) & 0xf); } else { tdir = (ob->temp1 & 0xf); if ((head->dir == (unsigned)tdir) && (ob->dir == (unsigned)tdir)) // increment // tried dir if robot will attempt to move at tdir => // head and body are at move try dir { //Debug("\ntrying next queue dir %d",tdir); MISCVARS->NMEdirstried ++; if (MISCVARS->NMEdirstried == MISCVARS->NMEqueuesize) //gone through all queue entries { //Debug("\nqueue exhausted"); ob->temp1 = -1; MISCVARS->NMEdirstried = 0; goto findplayer; } } } if (tdir != olddir) //rotate head to new chase direction { nextdir = dirorder16[olddir][NEXT]; prevdir = dirorder16[olddir][PREV]; if (dirdiff16[tdir][nextdir] < dirdiff16[tdir][prevdir]) head->dir = nextdir; else head->dir = prevdir; return; } //Debug("\nhead aligned to dir %d",tdir); //oddir = ob->dir; if (ob->dir != head->dir) // align body and wheels with head { ZEROMOM; NewState(wheels,&s_NMEwheels120); //rotate wheels for spinning nextdir = dirorder16[ob->dir][NEXT]; prevdir = dirorder16[ob->dir][PREV]; if (dirdiff16[head->dir][nextdir] < dirdiff16[head->dir][prevdir]) ob->dir = nextdir; else ob->dir = prevdir; wheels->dir = ob->dir; return; } // Debug("\nbody aligned to head at dir %d",ob->dir); ZEROMOM; ParseMomentum(ob,dirangle16[head->dir]); // Debug("\ntrying to move at dir %d",head->dir); ActorMovement(ob); UpdateNMELinkedActors(ob); if (ob->momentumx || ob->momentumy) { NewState(wheels,&s_NMEwheels2); // align wheels for movement //Debug("\nmove at dir %d succesful, resetting queue",head->dir); ob->temp1 = -1; //clear direction queue return; } else if (ob->temp1 == -1) // if queue is empty //make a queue of directions (byte packed) { //Debug("\nmove at dir %d failed and queue empty",head->dir); ob->temp1 = 0; MISCVARS->NMEdirstried = 0; MISCVARS->NMEqueuesize = 0; nextdir = ((tdir + 6) & 0xf); prevdir = ((tdir - 6) & 0xf); for(; MISCVARS->NMEqueuesize < 6; MISCVARS->NMEqueuesize += 2) { ob->temp1 <<= 4; ob->temp1 += nextdir; ob->temp1 <<= 4; ob->temp1 += prevdir; nextdir = ((nextdir-2) & 0xf); prevdir = ((prevdir+2) & 0xf); } } else // else goto next queue dir; { ob->temp1 >>= 4; } } void T_NME_Explode(objtype*ob) { if (ob->ticcount == 35) { objtype*head; int op; head = (objtype*)(ob->whatever); op = FixedMul(GRAVITY,(head->z-25)<<16) << 1; head->momentumz = -FixedSqrtHP(op); head->momentumx = (GameRandomNumber("NME head momx",0) << 2); head->momentumy = (GameRandomNumber("NME head momy",0) << 2); head->hitpoints = 0; head->flags |= FL_DYING; NewState(head,&s_shootinghead); //RemoveObj((objtype*)(ob->whatever)); // remove head } else if (!ob->ticcount) { ob->shapeoffset = 0; NewState(ob,&s_explosion1); SetGibSpeed(0x4000); SpawnParticles(ob,gt_sparks,200); ResetGibSpeed(); RemoveObj((objtype*)(ob->target)); } } void T_NME_HeadShoot(objtype*ob) { //int randtheta,i,offx,offy; ob->z += (ob->momentumz>>16); /*if (ob->momentumz < 0) {for(i=0;i<3;i++) {randtheta = (GameRandomNumber("NME spark drop",0) << 3); SpawnNewObj(ob->tilex,ob->tiley,&s_particle1,inertobj); new->temp2 = 1; offx = FixedMul(0x400,costable[randtheta]); offy = -FixedMul(0x400,sintable[randtheta]); new->x = new->drawx = ob->x + offx; new->y = new->drawy = ob->y + offy; new->z = ob->z-15; new->flags |= (FL_NOFRICTION|FL_CRAZY|FL_ABP); new->dir = west; MakeActive(new); } }*/ ob->momentumz += GRAVITY; if (ob->z >= (nominalheight+45)) { ob->z = nominalheight+45; if (ob->temp2) { ob->momentumz = -30000*ob->temp2; ob->temp2--; } else { ob->momentumx = ob->momentumy = ob->momentumz = 0; ob->shapeoffset = 0; NewState(ob,&s_NMEheadexplosion); return; } } ActorMovement(ob); } boolean NMEspincheck(objtype*ob) { int dx,dy,dz; dx = abs(PLAYER[0]->x - ob->x); dy = abs(PLAYER[0]->y - ob->y); dz = abs(PLAYER[0]->z - ob->z); if ((dx < 0x10000) && (dy < 0x10000) && (dz < 32)) { NewState(ob,&s_NMEspinattack); NewState((objtype*)(ob->target),&s_NMEwheelspin); if (!ob->temp2) NewState((objtype*)(ob->whatever),&s_NMEhead1); else NewState((objtype*)(ob->whatever),&s_NMEhead2); ob->dirchoosetime = 1; return true; } return false; } void T_NME_SpinAttack(objtype* ob) { int mx,my,mz; objtype*head,*wheels; if (ob->ticcount == 30) // knock player back { GetMomenta(PLAYER[0],ob,&mx,&my,&mz,0x4000); DamageThing(PLAYER[0],20); Collision(PLAYER[0],ob,mx,my); M_CheckPlayerKilled(PLAYER[0]); } if (ob->dirchoosetime) ob->dirchoosetime --; else { head = (objtype*)(ob->whatever); wheels = (objtype*)(ob->target); wheels->dir = head->dir = ob->dir = dirorder16[dirorder16[ob->dir][NEXT]][NEXT]; ob->dirchoosetime = 1; } } void T_NME_SpinFire(objtype*ob) { int randtheta,oldyzangle,dx,dy,xydist,dz; objtype *head,*wheels; head = (objtype*)(ob->whatever); wheels = (objtype*)(ob->target); if (ob->dir != (unsigned)ob->targettilex) { ob->dir = head->dir = wheels->dir = dirorder16[ob->dir][ob->temp3]; return; } if (ob->dirchoosetime) { ob->dirchoosetime --; return; } if (ob->temp3 < 20) { //randphi = (GameRandomNumber("NME generate phi",0) << 3) & ((ANGLES/2) -1); if (GameRandomNumber("NME generate theta",0) < 128) randtheta = (GameRandomNumber("NME generate theta",0)>>4); else randtheta = -(GameRandomNumber("NME generate theta",0)>>4); dx = PLAYER[0]->x-ob->x; dy = ob->y-PLAYER[0]->y; if (GameRandomNumber("bcraft shoot up/down",0) < 128) dz = 5; else dz = -5; xydist = FindDistance(dx,dy); randtheta += atan2_appx(dx,dy); Fix(randtheta); oldyzangle = ob->yzangle; ob->yzangle = atan2_appx(xydist,dz<<10); //ob->yzangle = randphi; SD_PlaySoundRTP(BAS[ob->obclass].fire+1,ob->x,ob->y); //wheels->dir = head->dir = ob->dir = dirorder16[dirorder16[ob->dir][NEXT]][NEXT]; SpawnMissile(ob,fireballobj,0x6000,randtheta,&s_NMEminiball1,0x10000); ob->dirchoosetime = 1; ob->yzangle = oldyzangle; ob->temp3 ++; } else { ob->temp3 = 0; NewState(ob,&s_NMEchase); NewState((objtype*)(ob->target),&s_NMEwheels2); if (!ob->temp2) NewState((objtype*)(ob->whatever),&s_NMEhead1); else NewState((objtype*)(ob->whatever),&s_NMEhead2); } } void T_NME_Attack(objtype*ob) { int angle,perpangle,i; if (NMEspincheck(ob)) { //ob->temp3 = 0; return; } if (ob->dirchoosetime) { ob->dirchoosetime --; return; } if (!CheckLine(ob,PLAYER[0],SIGHT)) { //ob->temp3 = 0; NewState(ob,&s_NMEchase); NewState((objtype*)(ob->target),&s_NMEwheels2); if (!ob->temp2) NewState((objtype*)(ob->whatever),&s_NMEhead1); else NewState((objtype*)(ob->whatever),&s_NMEhead2); return; } //sound = BAS[ob->obclass].fire; angle = AngleBetween(ob,PLAYER[0]); if ((ob->temp3 == 0) || (ob->temp3 == 1)) //heatseek { SD_PlaySoundRTP(BAS[ob->obclass].fire+2,ob->x,ob->y); angle = AngleBetween(ob,PLAYER[0]); SpawnMissile(ob,missileobj,0x6000,angle,&s_missile1,0x8000); if (ob->temp3 == 3) perpangle = angle + ANGLES/4; else perpangle = angle - ANGLES/4; Fix(perpangle); new->temp1 = NME_HEATSEEKINGTYPE; SetFinePosition(new,new->x + FixedMul(0x8000l,costable[perpangle]), new->y - FixedMul(0x8000l,sintable[perpangle])); SetVisiblePosition(new,new->x,new->y); if (!ob->temp3) ob->dirchoosetime = 20; else { ob->dirchoosetime = 35 - (ob->shapeoffset >> 2) - (gamestate.difficulty << 2);//70; if (!ob->temp2) NewState((objtype*)(ob->whatever),&s_NMEhead1); else NewState((objtype*)(ob->whatever),&s_NMEhead2); } ob->temp3 ++; } else if (ob->temp3 == 2) // saucer { SpawnMissile(ob,NMEsaucerobj,0x1000,angle,&s_NMEsaucer1,0xc000); new->flags |= FL_SHOOTABLE; ob->temp3++; ob->dirchoosetime = 35 - (ob->shapeoffset >> 2) - (gamestate.difficulty << 2);//70; if (!ob->temp2) NewState((objtype*)(ob->whatever),&s_NMEhead1rl); else NewState((objtype*)(ob->whatever),&s_NMEhead2rl); } else if ((ob->temp3 == 3) || (ob->temp3 == 4)) // drunk { SD_PlaySoundRTP(BAS[ob->obclass].fire+2,ob->x,ob->y); if (!ob->temp3) perpangle = angle + ANGLES/4; else perpangle = angle - ANGLES/4; Fix(perpangle); for(i=0; i<(2+gamestate.difficulty); i++) { SpawnMissile(ob,missileobj,0x6000,angle,&s_missile1,0x8000); new->temp1 = NME_DRUNKTYPE; SetFinePosition(new,new->x + FixedMul(0x8000l,costable[perpangle]), new->y - FixedMul(0x8000l,sintable[perpangle])); SetVisiblePosition(new,new->x,new->y); } if (ob->temp3 == 3) ob->dirchoosetime = 20; else { ob->temp3 = 0; NewState(ob,&s_NMEchase); if (!ob->temp2) NewState((objtype*)(ob->whatever),&s_NMEhead1); else NewState((objtype*)(ob->whatever),&s_NMEhead2); } ob->temp3 ++; } } //================== Tom/Snake ============================================ void T_DarkSnakeSpawn(objtype*ob) { objtype * linkinfront; if (((ob->state == &s_darkmonkhspawn) && (!(ob->ticcount%8))) || ((ob->state == &s_darkmonkfastspawn) && (!(ob->ticcount%4)))) { GetNewActor(); MakeActive(new); SetFinePosition(new,ob->x,ob->y); SetVisiblePosition(new,ob->x,ob->y); new->z = nominalheight; new->areanumber = MAPSPOT(new->tilex,new->tiley,0)-AREATILE; MakeLastInArea(new); new->obclass = b_darksnakeobj; new->which = ACTOR; new->angle = AngleBetween(ob,PLAYER[0]); new->dir = angletodir[new->angle]; if (SNAKELEVEL == 1) new->speed = 0x5000; else if (SNAKELEVEL == 2) new->speed = 0x5800; else new->speed = 0x2000; new->hitpoints = 1000; new->dirchoosetime = 0; new->door_to_open = -1; new->flags |= (FL_ABP|FL_NOFRICTION|FL_SHOOTABLE|FL_BLOCK); if (ob->whatever) { linkinfront = (objtype*)(ob->whatever); linkinfront->whatever = new; new->target = linkinfront; new->targettilex = linkinfront->x; new->targettiley = linkinfront->y; new->angle = AngleBetween(new,linkinfront); new->dir = angletodir[new->angle]; new->flags |= FL_NEVERMARK; ParseMomentum(new,new->angle); NewState(new,&s_darkmonksnakelink); } else { SNAKEHEAD = new; if (SNAKELEVEL == 3) NewState(new,&s_darkmonkhead); else if (SNAKELEVEL == 1) { NewState(new,&s_snakefindpath); new->flags |= FL_ATTACKMODE; } else if (SNAKELEVEL == 2) { NewState(new,&s_snakepath); new->angle = 3*ANGLES/4; new->dir = angletodir[new->angle]; new->flags |= FL_ATTACKMODE; } ob->targettilex = ob->targettiley = 0; ParseMomentum(new,new->angle); } if (!ob->ticcount) SNAKEEND = new; ob->whatever = new; } } void T_GenericMove(objtype*ob) { int dx,dy; if (ob->temp3 == -1) return; if (!(SNAKEHEAD->flags & FL_ATTACKMODE)) return; if (ob->hitpoints <= 0) { KillActor(ob); ob->temp3 = 0; return; } if (!ob->ticcount) { if (ob->state == &s_darkmonkredlink) ob->temp3 = 0; else if ((ob!=SNAKEEND) && (ob->state == &s_redlinkhit)) NewState((objtype*)(ob->whatever),&s_redlinkhit); } dx = ob->targettilex-ob->x; dy = ob->y-ob->targettiley; if ((dx > -0xa000) && (dx < 0xa000) && (dy > -0xa000) && (dy < 0xa000)) { if (ob->temp1 && ob->temp2) { dx = ob->temp1 - ob->x; dy = ob->y - ob->temp2; ZEROMOM; /* if ((ob->targettilex == ob->temp1) && (ob->targettiley == ob->temp2)) return; */ //ob->x = ob->drawx = ob->targettilex; //ob->y = ob->drawy = ob->targettiley; //ob->tilex = ob->x >> TILESHIFT; //ob->tiley = ob->y >> TILESHIFT; ob->targettilex = ob->temp1; ob->targettiley = ob->temp2; ob->angle = atan2_appx(dx,dy); ob->dir = angletodir[ob->angle]; ParseMomentum(ob,ob->angle); } } else if (NOMOM) { //SNAKEHEAD->dirchoosetime = 0; ParseMomentum(ob,ob->angle); } if (ob->momentumx || ob->momentumy) MoveActor(ob); // ActorMovement(ob); } /* =============== = = SelectSnakeDir = =============== */ void SelectSnakeDir (objtype *ob) { int spot,centerx,centery,dx,dy; spot = MAPSPOT(ob->tilex,ob->tiley,1)-ICONARROWS; if ((spot >= 0) && (spot<= 7) && ((ob->dir!=(unsigned)spot)||(!(ob->flags & FL_DONE)))) { centerx= (ob->tilex << 16) + HALFGLOBAL1; centery= (ob->tiley << 16) + HALFGLOBAL1; dx = abs(centerx - ob->x); dy = abs(centery - ob->y); if ((dx < SNAKERAD) && (dy < SNAKERAD)) // new direction { ZEROMOM; ob->dir = spot; ob->flags |= FL_DONE; ParseMomentum(ob,dirangle8[ob->dir]); SetFinePosition(ob,centerx,centery); SetVisiblePosition(ob,ob->x,ob->y); if (ob==SNAKEHEAD) { SoftError("\n path changed at %d, %d",ob->tilex,ob->tiley); } } } MoveActor(ob); } void T_SnakePath(objtype*ob) { objtype*temp,*follower; if (SNAKEEND && (SNAKELEVEL == 2)) { if (CheckLine(SNAKEEND,PLAYER[0],SIGHT)) { if (ob->temp3 == -1) //if snake can see player //and he's presently stopped, restart { for(temp=ob; temp; temp=(objtype*)(temp->whatever)) { temp->temp3 = 0; temp->momentumx = temp->temp1; temp->momentumy = temp->temp2; } ob->dirchoosetime = 0; } } else if (ob->temp3 != -1) //else if he hasn't been stopped, stop him { for(temp=ob; temp; temp = (objtype*)(temp->whatever)) { temp->temp1 = temp->momentumx; temp->temp2 = temp->momentumy; temp->temp3 = -1; temp->momentumx = temp->momentumy = 0; } } else return; } if (ob->dirchoosetime) ob->dirchoosetime--; else { int count = 0; for(temp=ob; temp->whatever; temp=(objtype*)(temp->whatever)) { follower = (objtype*)(temp->whatever); follower->temp1 = temp->x; follower->temp2 = temp->y; SoftError("\n follower %d temp1 set to %4x, temp2 set to %4x", count,temp->x,temp->y); count ++; } ob->dirchoosetime = 2 ;//15 } if (ob->momentumx || ob->momentumy) SelectSnakeDir(ob); //else // {ParseMomentum(ob,ob->angle); // MoveActor(ob); // } } void FindClosestPath(objtype*ob) { int tx,ty,dx,dy,angle; tx = (ob->targettilex << 16) + TILEGLOBAL/2; ty = (ob->targettiley << 16) + TILEGLOBAL/2; dx= tx - ob->x; dy= ob->y - ty; angle = atan2_appx(dx,dy); ZEROMOM; ParseMomentum(ob,angle); MoveActor(ob); } void T_SnakeFindPath(objtype*ob) { int i,dx,dy,currdist,mindist,map; tpoint dstruct,*dummy=&dstruct; objtype*temp,*follower; if (ob->targettilex || ob->targettiley) { FindClosestPath(ob); dx = ob->targettilex - ob->tilex; dy = ob->targettiley - ob->tiley; if ((!dx) && (!dy)) { SetTilePosition(ob,ob->tilex,ob->tiley); SetVisiblePosition(ob,ob->x,ob->y); ob->y = ob->drawy = (ob->tiley << TILESHIFT) + TILEGLOBAL/2; NewState(ob,&s_snakepath); return; } } else { dummy->which = ACTOR; mindist = 0x7fffffff; for(i=0; i<whichpath; i++) { SetTilePosition(dummy,SNAKEPATH[i].x,SNAKEPATH[i].y); dummy->z = ob->z; if (CheckLine(ob,dummy,SIGHT)) { currdist = FindDistance(ob->tilex-dummy->tilex,ob->tiley-dummy->tiley); map = MAPSPOT(ob->tilex,ob->tiley,0)-AREATILE; if ((currdist < mindist) && (map >= 0) && (map <= NUMAREAS)) { ob->targettilex = dummy->tilex; ob->targettiley = dummy->tiley; mindist = currdist; } } } } if (ob->dirchoosetime) ob->dirchoosetime--; else { for(temp=ob; temp->whatever; temp=(objtype*)(temp->whatever)) { follower = (objtype*)(temp->whatever); follower->temp1 = temp->x; follower->temp2 = temp->y; } ob->dirchoosetime = 2 ;//15 } } void T_SnakeFinale(objtype*ob) { if ((ob->state == &s_snakefireworks1)||(ob->state == &s_snakefireworks2)) { if (ob->z != (maxheight-200)) { ob->z --; return; } SetGibSpeed(0x4500); SpawnParticles(ob,RANDOM,100); SpawnParticles(ob,gt_spit,100); ResetGibSpeed(); NewState(ob,&s_dexplosion1); } else { if (!ob->ticcount) { NewState(EXPLOSIONS,&s_megaremove); // SpawnParticles(ob,RANDOM,100); // SpawnParticles(ob,SPIT,100); return; } if (ob->dirchoosetime) ob->dirchoosetime --; else { ob->dirchoosetime = (GameRandomNumber("snake finale choose",0) % 7) + 15; SetGibSpeed(0x3000); SpawnParticles(ob,RANDOM,30); SpawnParticles(ob,gt_spit,20); ResetGibSpeed(); } } } void T_DarkSnakeChase(objtype*ob) { objtype* temp,*follower; int tdir,angle; if (!(ob->flags & FL_ATTACKMODE)) { if (!(CheckSight(ob,player) || Near(ob,player,4))) return; else { ob->flags |= FL_ATTACKMODE; MU_StartSong(song_bosssee); } } if (ob->hitpoints <= 0) { MU_StartSong(song_bossdie); KillActor(ob); AddMessage("Oscuro defeated!",MSG_CHEAT); return; } angle = AngleBetween(ob,PLAYER[0]); tdir = angletodir[angle]; if (Near(ob,PLAYER[0],6) && (ob->dir == (unsigned)tdir) && (!(ob->state->condition & SF_DOWN))) { NewState(ob,&s_snakefire1); SD_PlaySoundRTP(SD_SNAKEREADYSND,ob->x,ob->y); } if (!ob->ticcount) { if (ob->state == &s_darkmonkredhead) ob->temp3 = 0; // no longer hitable else if ((ob->state == &s_redheadhit) && (ob != SNAKEEND)) NewState((objtype*)(ob->whatever),&s_redlinkhit); else if (ob->state->condition & SF_UP) { SpawnMissile(ob,dm_spitobj,0x6000,angle,&s_spit1,0x6000); SD_PlaySoundRTP(BAS[ob->obclass].fire,ob->x,ob->y); //new->z -= 5; } //spawn spit; } if (CheckLine(ob,PLAYER[0],SIGHT)) { ob->targettilex = PLAYER[0]->x; ob->targettiley = PLAYER[0]->y; } if (ob->dirchoosetime) { ob->dirchoosetime--; ActorMovement(ob); if (NOMOM) ob->dirchoosetime = 0; } else { //if (ob) for(temp=ob; temp->whatever; temp=(objtype*)(temp->whatever)) { follower = (objtype*)(temp->whatever); follower->temp1 = temp->x; follower->temp2 = temp->y; } SelectChaseDir(ob); ob->dirchoosetime = 7 ;//15 } } void T_DarkmonkReact(objtype*ob) { if (ob->z < nominalheight) { MISCVARS->monkz += MZADJUST; ob->z = nominalheight + (MISCVARS->monkz >> 16); //ob->z++; return; } else { int ocl; ocl = ob->temp3; if (ocl == p_kesobj) NewState(ob,&s_darkmonkabsorb1); else if (ocl == p_heatseekobj) NewState(ob,&s_darkmonkhball1); else if (ocl == p_firebombobj) NewState(ob,&s_darkmonkbreathe1); else NewState(ob,&s_darkmonkchase1); ob->dirchoosetime = 0; } } void T_DarkmonkCharge(objtype*ob) { int dx,dy; dx = abs(PLAYER[0]->x - ob->x); dy = abs(PLAYER[0]->y - ob->y); if ((dx < 0xa000) && (dy < 0xa000)) { DamageThing(PLAYER[0],10); Collision(PLAYER[0],ob,0,0); M_CheckPlayerKilled(PLAYER[0]); } if (!ob->ticcount) ob->speed >>= 1; if (ob->dirchoosetime) ob->dirchoosetime --; if (NOMOM || (!ob->dirchoosetime)) { ob->angle = AngleBetween(ob,PLAYER[0]); ob->dir = angletodir[ob->angle]; ParseMomentum(ob,ob->angle); ob->dirchoosetime = 5; } ActorMovement(ob); } void T_DarkmonkLandAndFire(objtype*ob) { if (ob->z < nominalheight) { MISCVARS->monkz += MZADJUST; ob->z = nominalheight + (MISCVARS->monkz >> 16); //ob->z++; return; } if (Near(ob,PLAYER[0],3)) { if (GameRandomNumber("darkmonkland",0)<128) NewState(ob,&s_darkmonkbball1); else { ob->angle = AngleBetween(ob,PLAYER[0]); ob->dir = angletodir[ob->angle]; ob->speed <<= 1; // goes twice as fast ZEROMOM; ParseMomentum(ob,ob->angle); ob->dirchoosetime = 5; // change dir every 5 tics ob->hitpoints -= 200; // big penalty for charging if (ob->hitpoints <= 0) { objtype*column = (objtype*)(ob->whatever); EnableObject((long)column); ob->whatever = NULL; KillActor(ob); NewState(ob,&s_darkmonkfastspawn); AddMessage("Oscuro flees!",MSG_CHEAT); return; } NewState(ob,&s_darkmonkcharge1); } } else if (ob->temp1) NewState(ob,&s_darkmonklightning1); else NewState(ob,&s_dmgreenthing1); ob->temp1 ^= 1; ob->dirchoosetime = 0; } void T_DarkmonkChase(objtype*ob) { int chance,dx,dy,dist; if (!Near(ob,PLAYER[0],2)) { if (ob->z > (maxheight - 100)) { MISCVARS->monkz -= MZADJUST; ob->z = nominalheight + (MISCVARS->monkz >> 16); //ob->z--; return; } } else if (ob->z < nominalheight) { MISCVARS->monkz += MZADJUST; ob->z = nominalheight + (MISCVARS->monkz >> 16); //ob->z++; return; } if (CheckLine(ob,PLAYER[0],SIGHT)) { ob->targettilex = PLAYER[0]->x; ob->targettiley = PLAYER[0]->y; } if (!ob->ticcount) { if (CheckLine(ob,PLAYER[0],SHOOT)) // got a shot at player? { dx = abs(ob->tilex - PLAYER[0]->tilex); dy = abs(ob->tiley - PLAYER[0]->tiley); dist = dx>dy ? dx : dy; if (!dist || dist==1) chance = 300;//300; else chance = 400/dist;//300/dist; if (GameRandomNumber("T_DarkMonkChase",0) < chance) { NewState(ob,&s_dmlandandfire); return; } } } if (ob->dirchoosetime) ob->dirchoosetime--; if ((ob->flags & FL_STUCK) || (!ob->dirchoosetime)) { SelectChaseDir(ob); ob->dirchoosetime = M_CHOOSETIME(ob); } else { if (NOMOM) ParseMomentum(ob,dirangle8[ob->dir]); ActorMovement(ob); } } //====================== End of Boss Functions ===========================// void T_GunStand(objtype*ob) { int dy,dx,infrontof,dz; objtype* temp; // if (ob->target) // Error("gun reset with non-null target"); for(temp = firstareaactor[ob->areanumber]; temp; temp= temp->nextinarea) { if (temp == ob) continue; if (temp->obclass == ob->obclass) continue; if ((!(temp->flags & FL_SHOOTABLE)) || (temp->flags & FL_DYING)) continue; dy = ob->y - temp->y; dx = ob->x - temp->x; dz = ob->z - temp->z; if ((abs(dy)>0x40000) || (abs(dx)>0x40000) || (abs(dz) > 20)) continue; infrontof = 0; switch (ob->dir) { case north: if ((dy > 0) && (abs(dx)<0x8000)) infrontof = 1; break; case east: if ((dx < 0) && (abs(dy)<0x8000)) infrontof = 1; break; case south: if ((dy < 0) && (abs(dx)<0x8000)) infrontof = 1; break; case west: if ((dx > 0) && (abs(dy)<0x8000)) infrontof = 1; break; default: break; } if (infrontof && CheckLine(ob,temp,SHOOT)) { ob->target = temp; NewState(ob,&s_gunraise1); return; } } } void T_4WayGunStand(objtype*ob) { int dy,dx,dz; objtype* temp; if (ob->target) Error("gun reset with non-null target"); for(temp = firstareaactor[ob->areanumber]; temp; temp= temp->nextinarea) { if (temp == ob) continue; if (temp->obclass == ob->obclass) continue; if ((!(temp->flags & FL_SHOOTABLE)) || (temp->flags & FL_DYING)) continue; dy = abs(ob->x-temp->x); dx = abs(ob->y-temp->y); dz = abs(ob->z-temp->z); if ((dx < 0x40000) && (dy < 0x40000) && (dz< 20) && CheckLine(ob,temp,SHOOT)) { //if ((dx < 0x8000) || (dy <0x8000)) ob->target = temp; NewState(ob,&s_4waygunfire1); return; } } } void A_GunShoot(objtype*ob) { int dx,dy,dz,damage,infrontof,tnear,savedangle; objtype * target; if (!ob->ticcount) { target = (objtype*)(ob->target); if (!target) Error("an instance of %s called gunshoot without a target\n",debugstr[ob->obclass]); if ((!(target->flags & FL_SHOOTABLE)) || (target->flags & FL_DYING)) { NewState(ob,&s_gunlower1); ob->target = NULL; return; } dx = target->x-ob->x; dy = ob->y-target->y; dz = ob->z-target->z; tnear = ((abs(dy)<0x40000) && (abs(dx)<0x40000) && (abs(dz) < 20)); infrontof = 0; switch (ob->dir) { case north: if ((dy > 0) && (abs(dx)<0x8000)) infrontof = 1; break; case east: if ((dx < 0) && (abs(dy)<0x8000)) infrontof = 1; break; case south: if ((dy < 0) && (abs(dx)<0x8000)) infrontof = 1; break; case west: if ((dx > 0) && (abs(dy)<0x8000)) infrontof = 1; break; default: break; } if ((!infrontof) || (!CheckLine(ob,target,SHOOT)) || (!tnear)) { NewState(ob,&s_gunlower1); ob->target = NULL; return; } //SD_PlaySoundRTP(SD_FIRE,PLAYER[0]->x,PLAYER[0]->y,ob->x,ob->y); //hitchance = 128; // if (!target) // Error("object called shoot without a target\n"); damage = DMG_AHGUN; if (target->obclass == playerobj) { target->target = ob; if (target->flags & FL_BPV) damage >>= 1; } savedangle = ob->angle; ob->angle = atan2_appx(dx,dy); RayShoot(ob,damage,GameRandomNumber("A_GunShoot Accuracy",0) % 20); ob->angle = savedangle; SD_PlaySoundRTP(SD_BIGEMPLACEFIRESND,ob->x,ob->y); } } void A_4WayGunShoot(objtype*ob) { int dx,dy,dz,damage,savedangle; objtype * target; if (ob->ticcount == (ob->state->tictime >> 1)) { target = (objtype*)(ob->target); if (!target) Error("an instance of %s called 4waygunshoot without a target\n",debugstr[ob->obclass]); dx = abs(target->x-ob->x); dy = abs(ob->y-target->y); dz = abs(ob->z-target->z); if ((dx > 0x40000) || (dy > 0x40000) || (dz > 20) || (!CheckLine(ob,target,SHOOT)) || (!(target->flags & FL_SHOOTABLE)) || (target->flags & FL_DYING) ) { ob->target = NULL; NewState(ob,&s_4waygun); return; } //SD_PlaySoundRTP(SD_FIRE,PLAYER[0]->x,PLAYER[0]->y,ob->x,ob->y); //hitchance = 128; // if (!target) // Error("object called shoot without a target\n"); damage = DMG_AHGUN; SD_PlaySoundRTP(BAS[ob->obclass].fire,ob->x,ob->y); if (target->obclass == playerobj) { target->target = ob; if (target->flags & FL_BPV) damage >>= 1; } savedangle = ob->angle; ob->angle = 0; RayShoot(ob,damage,GameRandomNumber("A_4WayGunShoot Accuracy",0) % 20); ob->angle = ANG90; RayShoot(ob,damage,GameRandomNumber("A_4WayGunShoot Accuracy",0) % 20); ob->angle = ANG180; RayShoot(ob,damage,GameRandomNumber("A_4WayGunShoot Accuracy",0) % 20); ob->angle = ANG270; RayShoot(ob,damage,GameRandomNumber("A_4WayGunShoot Accuracy",0) % 20); ob->angle = savedangle; } } void A_Drain (objtype *ob) { int dx,dy,dz,damage; dx = abs(PLAYER[0]->x - ob->x); dy = abs(PLAYER[0]->y - ob->y); dz = abs(PLAYER[0]->z - ob->z); if ((dx > TOUCHDIST) || (dy > TOUCHDIST) || (dz > (TOUCHDIST>>10))) { NewState(ob,&s_dmonkshoot5); return; } if (ob->ticcount) return; else { damage = (GameRandomNumber("A_Drain",ob->obclass) >> 3); DamageThing (PLAYER[0],damage); ob->hitpoints += damage; if (ob->hitpoints > starthitpoints[gamestate.difficulty][ob->obclass]) ob->hitpoints = starthitpoints[gamestate.difficulty][ob->obclass]; Collision(PLAYER[0],ob,0,0); if (PLAYER[0]->flags & FL_DYING) PLAYER[0]->target = ob; M_CheckPlayerKilled(PLAYER[0]); SD_PlaySoundRTP(SD_MONKGRABSND,ob->x,ob->y); } } void A_DmonkAttack(objtype*ob) { int angle,nobclass,nspeed,altangle1=0,altangle2=0,zoff=0,sound; statetype *nstate; if (!ob->ticcount) { ob->hitpoints -= 120;//120; if (ob->hitpoints <= 0) { objtype*column = (objtype*)(ob->whatever); EnableObject((long)column); ob->whatever = NULL; KillActor(ob); NewState(ob,&s_darkmonkfastspawn); return; } } if (ob->dirchoosetime) ob->dirchoosetime --; else { sound = BAS[ob->obclass].fire; angle = AngleBetween(ob,PLAYER[0]); nspeed = 0x6000; if (ob->state == &s_darkmonksphere8) { nstate = &s_kessphere1; nobclass = p_kesobj; ob->dirchoosetime = 70; } else if (ob->state == &s_darkmonkhball7) { nstate = &s_handball1; nobclass = dm_heatseekobj; nspeed = 0x3000; ob->dirchoosetime = 5; } else if (ob->state == &s_darkmonkbball7) { nstate = &s_faceball1; nobclass = dm_weaponobj; nspeed = 0x3000; ob->dirchoosetime = 5; } else if (ob->state == &s_darkmonklightning9) { nstate = &s_lightning; nobclass = dm_weaponobj; ob->dirchoosetime = 3; sound++; } else if (ob->state == &s_dmgreenthing8) { nstate = &s_energysphere1; nobclass = dm_weaponobj; sound +=2; ob->dirchoosetime = 70; } else if (ob->state == &s_darkmonkfspark5) { nstate = &s_floorspark1; altangle1 = angle + ANGLES/24; altangle2 = angle - ANGLES/24; Fix(altangle1); Fix(altangle2); nobclass = dm_weaponobj; ob->dirchoosetime = 3; sound += 3; } else if (ob->state == &s_darkmonkbreathe6) { nstate = &s_crossfire1; ob->dirchoosetime = 3; nobclass = dm_weaponobj; zoff = -15; sound += 3; } SpawnMissile(ob,nobclass,nspeed,angle,nstate,0xb000); SD_PlaySoundRTP(sound,ob->x,ob->y); new->z = ob->z+zoff; if (altangle1) { SpawnMissile(ob,nobclass,nspeed,altangle1,nstate,0xb000); SpawnMissile(ob,nobclass,nspeed,altangle2,nstate,0xb000); } } } #endif // SHAREWARE endif //=====================================================================// /* =============== = = T_Stand = =============== */ void T_Stand (objtype *ob) { if (!ob->ticcount) SightPlayer (ob); else SoftError("\n ob type %s ticcount of %d in T_Stand",debugstr[ob->obclass], ob->ticcount); } void DamagePlayerActor(objtype *ob, int damage) { playertype *pstate; switch (gamestate.difficulty) { case 0: damage >>= 1; break; case 1: damage -= (damage >> 2); break; case 2: break; case 3: //damage += (damage>>2); break; //default: Error("Um, Gamestate.Difficulty, uh, has problems.\n"); } if (!damage) damage++; M_LINKSTATE(ob,pstate); pstate->health -= damage; ob->hitpoints = pstate->health; SD_PlaySoundRTP(SD_PLAYERTCHURTSND+(pstate->player),ob->x,ob->y); if (ob==player) { damagecount += damage; if ( SHOW_BOTTOM_STATUS_BAR() ) DrawBarHealth (false); } if (pstate->health<=0) { pstate->health = 0; ob->hitpoints = 0; } } void DamageNonPlayerActor(objtype *ob,int damage) { //if ((ob->obclass == b_darksnakeobj) && (!ob->temp3)) // return; if (!(ob->flags & FL_ATTACKMODE)) damage <<= 1; ob->hitpoints -= damage; if (ob->hitpoints <= 0) { int sound; sound = BAS[ob->obclass].die; if (ob->obclass == lowguardobj) { if (ob->shapeoffset) sound ++; } SD_PlaySoundRTP(sound,ob->x,ob->y); } else SD_PlaySoundRTP(BAS[ob->obclass].hit,ob->x,ob->y); #if (SHAREWARE == 0) if ((ob->obclass == b_robobossobj) && (ob->temp2 <= 2)) { if (ob->hitpoints < ((3-ob->temp2)*starthitpoints[gamestate.difficulty][ob->obclass]>>2) ) { SD_PlaySoundRTP(SD_NMEAPARTSND,ob->x,ob->y); ob->temp2++; ob->shapeoffset += 16; ob->speed += 0x500; SpawnNewObj(ob->tilex,ob->tiley,&s_megaexplosions,inertobj); new->temp1 = 3; new->flags |= FL_ABP; MakeActive(new); } if (ob->temp2 == 1) NewState((objtype*)(ob->whatever),&s_NMEhead2); } #endif MISCVARS->madenoise = true; } void DamageStaticObject(statobj_t*tempstat,int damage) { tempstat->hitpoints -= damage; if (tempstat->hitpoints <= 0) { sprites[tempstat->tilex][tempstat->tiley]=NULL; tempstat->flags |= FL_NONMARK; if (tempstat->flags&FL_LIGHT) { if (MAPSPOT(tempstat->tilex,tempstat->tiley,2)) { touchplatetype *tplate; for(tplate=touchplate[tempstat->linked_to]; tplate; tplate = tplate->nextaction) if (tplate->whichobj == (long)(tempstat)) RemoveTouchplateAction(tplate,tempstat->linked_to); } if (tempstat->flags & FL_LIGHTON) TurnOffLight(tempstat->tilex,tempstat->tiley); if (tempstat->itemnumber<=stat_chandelier) //SpawnFallingDebris(tempstat->x,tempstat->y,tempstat->z-32); { objtype *prevlast = LASTACTOR; SpawnSlowParticles(gt_sparks,4,tempstat->x,tempstat->y,tempstat->z-32); for(prevlast = prevlast->next; prevlast; prevlast= prevlast->next) { prevlast->momentumz = 1; // any positive value will do prevlast->momentumx >>= 1; prevlast->momentumy >>= 1; } } else { SpawnStatic(tempstat->tilex,tempstat->tiley,stat_metalshards,-1); LASTSTAT->flags |= (FL_ABP|FL_NONMARK); sprites[tempstat->tilex][tempstat->tiley] = NULL; MakeStatActive(LASTSTAT); switch (tempstat->itemnumber) { case stat_lamp: case stat_altbrazier1: case stat_altbrazier2: case stat_torch: SpawnSlowParticles(gt_sparks,5,tempstat->x,tempstat->y,tempstat->z-32); break; case stat_floorfire: SpawnSlowParticles(gt_sparks,5,tempstat->x,tempstat->y,tempstat->z); break; default: ; } } SpawnSolidStatic(tempstat); SD_PlaySoundRTP(SD_ITEMBLOWSND,tempstat->x,tempstat->y); } else { switch (tempstat->itemnumber) { case stat_dariantouch: MISCVARS->ETOUCH[tempstat->linked_to].x = MISCVARS->ETOUCH[tempstat->linked_to].y = 0; case stat_tntcrate: case stat_bonusbarrel: SpawnNewObj(tempstat->tilex,tempstat->tiley,&s_staticexplosion1,inertobj); MakeActive(new); new->flags |= FL_ABP; new->whatever = tempstat; new->temp2 = damage; if (tempstat->itemnumber == stat_bonusbarrel) { int rand = GameRandomNumber("DamageThing",0); if (rand < 80) { if (rand & 1) SpawnStatic(tempstat->tilex,tempstat->tiley,stat_monkmeal,-1); else SpawnStatic(tempstat->tilex,tempstat->tiley,stat_priestporridge,-1); gamestate.healthtotal ++; } else if (rand < 160) { if (rand & 1) SpawnStatic(tempstat->tilex,tempstat->tiley,stat_lifeitem1,-1); else SpawnStatic(tempstat->tilex,tempstat->tiley,stat_lifeitem3,-1); } else { if (rand & 1) SpawnStatic(tempstat->tilex,tempstat->tiley,stat_mp40,-1); else { SpawnStatic(tempstat->tilex,tempstat->tiley,stat_heatseeker,-1); gamestate.missiletotal ++; } LASTSTAT->flags &= ~FL_RESPAWN; } //LASTSTAT->flags &= ~FL_SHOOTABLE; LASTSTAT->flags |= FL_ABP; MakeStatActive(LASTSTAT); SD_PlaySoundRTP(SD_BONUSBARRELSND,tempstat->x,tempstat->y); } else { ExplodeStatic(tempstat); if (tempstat == touchsprite) touchsprite = NULL; } SpawnSolidStatic(tempstat); //SD_Play(SD_EXPL); break; #if (SHAREWARE == 0) case stat_mine: SpawnNewObj(tempstat->tilex,tempstat->tiley,&s_grexplosion1,inertobj); MakeActive(new); new->flags |= FL_ABP; new->whatever = tempstat; new->temp2 = damage; RemoveStatic(tempstat); break; case stat_tomlarva: SD_PlaySoundRTP(SD_ACTORSQUISHSND,tempstat->x,tempstat->y); SpawnGroundExplosion(tempstat->x,tempstat->y,tempstat->z); //MISCVARS->gibgravity = GRAVITY/2; MISCVARS->fulllightgibs = true; SetGibSpeed(0x4000); SpawnSlowParticles(GUTS,30,tempstat->x, tempstat->y,tempstat->z); ResetGibSpeed(); MISCVARS->fulllightgibs = false; //MISCVARS->gibgravity = -1; RemoveStatic(tempstat); break; #endif case stat_lifeitem1: case stat_lifeitem2: case stat_lifeitem3: case stat_lifeitem4: SD_PlaySoundRTP(SD_ITEMBLOWSND,tempstat->x,tempstat->y); gamestate.treasurecount ++; SpawnSlowParticles(gt_sparks,10,tempstat->x,tempstat->y,tempstat->z); SpawnSolidStatic(tempstat); break; default: if ((tempstat->itemnumber == stat_plant) || (tempstat->itemnumber == stat_tree)) gamestate.plantcount++; //tempstat->shapenum = -1; //tempstat->flags &= ~FL_SHOOTABLE; ExplodeStatic(tempstat); SpawnSolidStatic(tempstat); break; } } } } void DamageThing (void *thing, int damage) { objtype* tempactor; statobj_t* tempstat; tempactor = (objtype*)thing; if (!tempactor) return; if ((tempactor->which == ACTOR) && (!(tempactor->flags & FL_SHOOTABLE))) return; if ((tempactor->which == ACTOR) || (tempactor->which == SPRITE)) { if (tempactor->which == ACTOR) { if (tempactor->obclass == playerobj) { if ((tempactor->flags & FL_GODMODE) || (tempactor->flags & FL_DOGMODE) || godmode || (gamestate.battlemode == battle_Eluder) ) return; DamagePlayerActor(tempactor,damage); } else { if ((tempactor->obclass == collectorobj) && (gamestate.SpawnEluder)) return; if (tempactor->hitpoints <= 0) return; DamageNonPlayerActor(tempactor,damage); } } else { tempstat = (statobj_t*)thing; MISCVARS->madenoise = true; if (!(tempstat->flags & FL_SHOOTABLE)) return; DamageStaticObject(tempstat,damage); } } } void ExplodeStatic(statobj_t*tempstat) { //SpawnSolidStatic(tempstat); if (tempstat->flags & FL_WOODEN) { SpawnStatic(tempstat->tilex,tempstat->tiley,stat_woodfrag,-1); if ((gamestate.BattleOptions.RespawnItems) && (tempstat->itemnumber == stat_tntcrate) ) { tempstat->linked_to = (long)(LASTSTAT); tempstat->flags |= FL_RESPAWN; } } else if (tempstat->flags & FL_METALLIC) SpawnStatic(tempstat->tilex,tempstat->tiley,stat_metalfrag,-1); else SpawnStatic(tempstat->tilex,tempstat->tiley,stat_rubble,-1); LASTSTAT->flags |= (FL_ABP|FL_NONMARK); sprites[tempstat->tilex][tempstat->tiley] = NULL; MakeStatActive(LASTSTAT); SD_PlaySoundRTP(SD_ITEMBLOWSND,tempstat->x,tempstat->y); } void EnableObject(long object) { objtype* ob; int i,gasicon; doorobj_t*tdoor; ob = (objtype*)object; #if (BNACRASHPREVENT == 1)// if (ob == 0) { return; } #endif ob->flags |= FL_ACTIVE; if (ob->obclass == bladeobj) { ParseMomentum(ob,dirangle8[ob->dir]); if (ob->whatever) { objtype *passenger=(objtype*)(ob->whatever); passenger->momentumx += ob->momentumx; passenger->momentumy += ob->momentumy; } } if (ob->obclass == gasgrateobj) { NewState(ob,&s_gas2); SD_PlaySoundRTP(SD_GASSTARTSND,ob->x,ob->y); ob->dirchoosetime = GASTICS; for(i=0; i<doornum; i++) { tdoor = doorobjlist[i]; gasicon = MAPSPOT(tdoor->tilex,tdoor->tiley,1); if (gasicon == GASVALUE) LinkedCloseDoor(i); } MU_StoreSongPosition(); MU_StartSong(song_gason); MISCVARS->GASON = 1; ob->temp3 = 105; } else if (ob->obclass == pillarobj) { ob->flags |= FL_FLIPPED; gamestate.secretcount++; } if (!(ob->flags & FL_ABP)) { ob->flags |= FL_ABP; MakeActive(ob); } } void DisableObject(long object) { objtype*ob; ob = (objtype*)object; ob->flags &= ~FL_ACTIVE; } void T_MoveColumn(objtype* ob) { int spot,index; if (!(ob->flags & FL_ACTIVE)) return; /* switch (ob->dir) {case north: ob->momentumy = -PILLARMOM; break; case south: ob->momentumy = PILLARMOM; break; case east: ob->momentumx = PILLARMOM; break; case west: ob->momentumx = -PILLARMOM; break; } */ ActorMovement(ob); index = touchindices[ob->tilex][ob->tiley]; if (index) TRIGGER[index-1] = 1; ob->temp1 -= PILLARMOM; if ((ob->temp1 <= 0) || NOMOM) { ZEROMOM; ob->temp1 = 0x20000; ob->flags &= ~FL_ACTIVE; spot = MAPSPOT(ob->tilex,ob->tiley,1)-ICONARROWS; if ((spot >= 0) && (spot <= 7)) { ob->dir = spot; if (!ob->temp2) { gamestate.secrettotal++; } else { ob->flags |= FL_ACTIVE; } ParseMomentum(ob,dirangle8[ob->dir]); } else ob->flags |= FL_DONE; } } boolean NextToDoor(objtype*ob) { int tilex,tiley,centerx,centery,dx,dy; tilex = ob->tilex; tiley = ob->tiley; if (M_ISDOOR(tilex+1,tiley) || M_ISDOOR(tilex-1,tiley)) { centery = (tiley << TILESHIFT) + HALFGLOBAL1; dy = abs(ob->y - centery); if (dy < 0x2000) return true; } if (M_ISDOOR(tilex,tiley+1) || M_ISDOOR(tilex,tiley-1)) { centerx = (tilex << TILESHIFT) + HALFGLOBAL1; dx = abs(ob->x - centerx); if (dx < 0x2000) return true; } return false; } /* ================= = = T_Use = ================= */ void T_Use(objtype*ob) { if (ob->ticcount) return; switch (ob->obclass) { #if (SHAREWARE == 0) case b_darianobj: if (touchsprite && !DoPanicMapping()) touchsprite->flags |= FL_ACTIVE; if ((!sprites[PLAYER[0]->tilex][PLAYER[0]->tiley]) && (ob->areanumber == PLAYER[0]->areanumber)) { SpawnNewObj(PLAYER[0]->tilex,PLAYER[0]->tiley,&s_dspear1,spearobj); new->flags |= (FL_ABP);//|FL_INVULNERABLE); new->z = 0; MakeActive(new); } ZEROMOM; ob->flags |= FL_STUCK; SD_PlaySoundRTP(SD_DARIANUSESND,ob->x,ob->y); //NewState(ob,&s_darianspears); break; #endif default: ; } } #define RollStart(ob,state,angle) \ { \ int oldspeed = ob->speed; \ \ ob->speed = ROLLMOMENTUM+0x200; \ NewState(ob,state); \ ParseMomentum(ob,angle); \ ob->speed = oldspeed; \ } \ void AvoidPlayerMissile(objtype*ob) { objtype *temp; int dx,dy,dz; int magangle,angle1,rollangle1,rollangle2,dangle1,dangle2; if (PLAYER0MISSILE == NULL) return; if (GameRandomNumber("scott missile avoid",0) > 160) return; if (ob->momentumz) return; if ((ob->state->think == T_Roll) || (ob->state->think == T_Reset)) return; temp = PLAYER0MISSILE; dx = abs(temp->x - ob->x); dy = abs(ob->y - temp->y); dz = abs(ob->z - temp->z); angle1 = AngleBetween(temp,ob); magangle = abs(temp->angle - angle1); if (magangle > VANG180) magangle = ANGLES - magangle; if ((magangle > ANGLES/48) || (dx > 0x50000) || (dy > 0x50000) || (dz > 32)) return; rollangle1 = angle1 + ANGLES/4; Fix(rollangle1); dangle1 = abs(temp->angle - rollangle1); if (dangle1 > VANG180) dangle1 = ANGLES - dangle1; rollangle2 = angle1 - ANGLES/4; Fix(rollangle2); dangle2 = abs(temp->angle - rollangle2); if (dangle2 > VANG180) dangle2 = ANGLES - dangle2; ob->momentumx = ob->momentumy = 0; if (dangle1 > dangle2) { RollStart(ob,&s_strikerollleft1,rollangle1); } else { RollStart(ob,&s_strikerollright1,rollangle2); } ob->flags |= FL_NOFRICTION; ob->target = PLAYER[0]; //SelectRollDir(ob); } /* ================= = = T_Chase = ================= */ void T_Chase (objtype *ob) { int dx,dy,dz,dist,chance; classtype ocl; statetype *temp; boolean doorok; ocl = ob->obclass; if ((ocl == deathmonkobj) || (ocl == blitzguardobj)) { dx = abs(PLAYER[0]->x - ob->x); dy = abs(ob->y - PLAYER[0]->y); dz = abs(ob->z - PLAYER[0]->z); if ((dx < TOUCHDIST) && (dy < TOUCHDIST) && (dz < (TOUCHDIST >> 10))) { #if (SHAREWARE == 0) if (ocl == deathmonkobj) { NewState(ob,&s_dmonkshoot1); STOPACTOR(ob); return; } else #endif if ((!ob->temp3) && (PLAYERSTATE[0].missileweapon != -1) && (PLAYERSTATE[0].missileweapon < wp_godhand)) { NewState(ob,&s_blitzsteal1); STOPACTOR(ob); return; } } } ob->flags &= ~FL_DODGE; //if (CheckLine(ob,PLAYER[0],DIRCHECK) && (ocl != roboguardobj)) if (!ob->ticcount) { if (CheckLine(ob,PLAYER[0],SIGHT)) // got a shot at player? { if (ocl != roboguardobj) { ob->targettilex = PLAYER[0]->x; ob->targettiley = PLAYER[0]->y; } } if (CheckLine(ob,PLAYER[0],SHOOT) && (!(player->flags&FL_DYING))) // got a shot at player? { dx = abs(ob->tilex - PLAYER[0]->tilex); dy = abs(ob->tiley - PLAYER[0]->tiley); dist = (dx>dy)?(dx):(dy); if ((!dist) || (dist==1)) chance = 300; else if (ocl >= b_darianobj) chance = 400/dist; else chance = 300/dist; if (GameRandomNumber("T_Chase",ocl) <chance) { if ((ocl == b_heinrichobj) && (Near(ob,PLAYER[0],4))) goto cdoor; ob->dir = angletodir[AngleBetween(ob,PLAYER[0])]; STOPACTOR(ob); #if (SHAREWARE == 0) if ((ocl == overpatrolobj) && (!Near(ob,PLAYER[0],3)) && (!PLAYERSTATE[0].NETCAPTURED) && (!MISCVARS->NET_IN_FLIGHT)) { NewState(ob,&s_opbolo1); MISCVARS->NET_IN_FLIGHT = true; return; } #endif if ((ocl == triadenforcerobj) && (!Near(ob,PLAYER[0],3))) { NewState(ob,&s_enforcerthrow1); return; } if ((temp=M_S(AIM)) != NULL) { if ((ob->flags & FL_HASAUTO) && (!ob->temp3)) ob->temp3 = (GameRandomNumber("T_Chase FL_HASAUTO",ocl) % 5) + 3; ob->target = PLAYER[0]; NewState(ob,temp); return; } } //if ((CheckSight(ob,PLAYER[0])) && (!ob->angle))// && //(ocl != b_heinrichobj)) //ob->flags |= FL_DODGE; } } cdoor: doorok = NextToDoor(ob); if (ob->dirchoosetime) ob->dirchoosetime--; if ((ob->flags & FL_STUCK) || (!ob->dirchoosetime) || doorok) { //if (ob->flags & FL_DODGE) // SelectDodgeDir(ob); //else SelectChaseDir(ob); ob->dirchoosetime = M_CHOOSETIME(ob); } else { if (NOMOM) ParseMomentum(ob,dirangle8[ob->dir]); ActorMovement(ob); } } void SpawnMissile(objtype* shooter,classtype nobclass,int nspeed,int nangle,statetype*nstate,int offset) { GetNewActor(); MakeActive(new); new->which = ACTOR; new->obclass = nobclass; new->angle = nangle; new->speed = nspeed; if (shooter->obclass == playerobj) offset += FindDistance(shooter->momentumx,shooter->momentumy); SetFinePosition(new,shooter->x + FixedMul(offset,costable[nangle]), shooter->y - FixedMul(offset,sintable[nangle])); SetVisiblePosition(new,new->x,new->y); //SoftError("\n missx:%d, missy:%d, speed:%d, offset:%d, angle%d, drawx:%d, drawy:%d", // new->x,new->y,nspeed,offset,nangle,new->drawx,new->drawy); new->z = shooter->z; new->areanumber = shooter->areanumber; new->soundhandle = -1; if (nobclass != inertobj) { MakeLastInArea(new); if (MissileSound == true) new->soundhandle = SD_PlaySoundRTP(BAS[new->obclass].operate,new->x,new->y); } if ((shooter->obclass == playerobj) || (shooter->obclass == wallopobj) || (shooter->obclass == b_robobossobj)) { Set_3D_Momenta(new,new->speed,new->angle,shooter->yzangle); if (nobclass == p_drunkmissileobj) new->temp1 = new->momentumz; new->z -= FixedMulShift(offset,sintable[shooter->yzangle],26); if ((shooter->obclass == playerobj) && (shooter->flags & FL_GODMODE)) new->z -= 10; } else ParseMomentum(new,new->angle); if (nobclass == p_drunkmissileobj) new->temp3 = VBLCOUNTER/3; new->flags |= (FL_NEVERMARK|FL_ABP|FL_NOFRICTION|FL_FULLLIGHT); new->whatever = shooter; // keep track of missile possession NewState(new,nstate); } /* ================= = = T_Roll = ================= */ void T_Roll (objtype *ob) { ActorMovement(ob); } /* =============== = = T_Path = =============== */ void T_Path (objtype *ob) { int dx,dy,dz,ocl,damage=1; objtype*temp,*ttarg,*twhat; ocl = ob->obclass; if (((ocl == firejetobj) || (ocl == bladeobj)) && (!ob->ticcount)) { if (ocl == bladeobj) { if (ob->state->condition & SF_DOWN ) ob->flags &= ~FL_BLOCK; else if (ob->state->condition & SF_UP) { ob->flags |= FL_BLOCK; damage = 0; } } if ((ob->state->condition & SF_SOUND) && areabyplayer[ob->areanumber]) SD_PlaySoundRTP(BAS[ob->obclass].operate,ob->x,ob->y); if (damage) { for(temp=firstareaactor[ob->areanumber]; temp; temp=temp->nextinarea) { if (temp == ob) continue; if (temp->obclass >= roboguardobj) continue; //WAS ttarg = (objtype*)(temp->target); twhat = (objtype*)(temp->whatever); if ((M_ISACTOR(ttarg) && (ttarg->obclass == b_robobossobj)) || (M_ISACTOR(twhat) && (twhat->obclass == b_robobossobj)) ) continue; if ((!(temp->flags & FL_SHOOTABLE)) || (temp->flags & FL_DYING)) continue; if (temp->obclass == playerobj) { if ((temp->flags & FL_GODMODE) || (temp->flags & FL_DOGMODE)) continue; if ((temp->flags & FL_AV) && (ocl == firejetobj)) continue; } dx = temp->x - ob->x; if (abs(dx) > 0xa000) continue; dy = temp->y - ob->y; if (abs(dy) > 0xa000) continue; //if (temp->obclass == playerobj) //Collision(temp,-temp->momentumx+ob->momentumx,-temp->momentumy + ob->momentumy); dz = temp->z - ob->z; if (abs(dz) > 32) continue; DamageThing(temp,EnvironmentDamage(ob)); if ((ocl == firejetobj) && (temp->obclass < roboguardobj)) SD_PlaySoundRTP(SD_PLAYERBURNEDSND,temp->x,temp->y); if ((gamestate.violence == vl_excessive) && (temp->obclass < roboguardobj)) { if (ocl == bladeobj) { SpawnParticles(temp,GUTS,1); if (temp->hitpoints <= 0) temp->flags |= FL_HBM; } else if (ocl == firejetobj) { if ((temp->hitpoints <= 0) && (temp->z == nominalheight)) { temp->hitpoints = 0; temp->flags |= FL_SKELETON; if (temp->obclass == playerobj) { playertype *pstate; temp->flags &= ~FL_COLORED; M_LINKSTATE(temp,pstate); pstate->health = 0; pstate->weapon = -1; } Collision(temp,ob,-temp->momentumx,-temp->momentumy); M_CheckPlayerKilled(temp); continue; } } } //SD_PlaySoundRTP(SD_ACTORBURNEDSND,temp->x,temp->y); // if ((ocl == bladeobj) || (ob->state->condition == SF_CRUSH)) Collision(temp,ob,-temp->momentumx,-temp->momentumy); M_CheckPlayerKilled(temp); } } } if (ob->dir == nodir) return; if ((ocl != firejetobj) && (ocl != bladeobj) && (ocl != diskobj)) { if (!ob->ticcount) { if (SightPlayer (ob)) return; } else SoftError("\n ob type %s with ticcount %d in T_Path", debugstr[ob->obclass],ob->ticcount); } SelectPathDir (ob); if (NOMOM) ParseMomentum(ob,dirangle8[ob->dir]); } int EnvironmentDamage(objtype *ob) { if (BATTLEMODE && (gamestate.BattleOptions.DangerDamage != bo_danger_normal)) { return(gamestate.BattleOptions.DangerDamage); } else { int damage = 1; switch(ob->obclass) { case firejetobj: case bladeobj: damage = 6; break; case boulderobj: damage = 50; break; case spearobj: damage = 7; break; case gasgrateobj: damage = 20; break; case wallfireobj: damage = ((GameRandomNumber("wallfire damage",0) >>3) + 10); break; case crushcolobj: damage = 10; break; default: ; } if (gamestate.difficulty < gd_hard) damage >>= 1; return damage; } //SoftError("unknown environment danger"); } void T_AutoShootAlign(objtype*ob) { if (ob->dir != (dirtype)ob->temp1) ob->dir = dirorder16[ob->dir][NEXT]; else NewState(ob,M_S(AIM)); } void T_AutoRealign(objtype*ob) { if (ob->dir != (dirtype)ob->targettilex) ob->dir = dirorder16[ob->dir][NEXT]; else { objtype *temp; NewState(ob,M_S(PATH)); for(temp=firstareaactor[ob->areanumber]; temp; temp=temp->nextinarea) { if (temp == ob) continue; if (temp->obclass != ob->obclass) continue; if (!temp->state->think) NewState(temp,UPDATE_STATES[PATH][temp->obclass-lowguardobj]); } } } /* =============== = = T_AutoPath = =============== */ void T_AutoPath (objtype *ob) { objtype *temp; // ob->temp3 holds random number of shots before resuming path if (CheckLine(ob,PLAYER[0],SIGHT) && (Near(ob,PLAYER[0],4) || MISCVARS->madenoise)) { int dx,dy,destdir,ocl; statetype *align,*wait; ocl = ob->obclass; dx = player->x - ob->x; dy = ob->y - player->y; destdir = (angletodir[atan2_appx(dx,dy)] << 1); ob->temp1 = destdir; ob->targettilex = ob->dir; //save old dir #if (SHAREWARE == 0) if (ocl == wallopobj) { //if (ob->temp3) // Error("may be writing over temp3"); ob->temp3 = (GameRandomNumber("T_WallPath",0)%4) + 1; align = &s_wallalign; wait = &s_wallwait; } else #endif { align = &s_roboalign; wait = &s_robowait; } NewState(ob,align); for(temp=firstareaactor[ob->areanumber]; temp; temp=temp->nextinarea) { if (temp == ob) continue; if (temp->obclass != ob->obclass) continue; if (temp->flags & FL_DYING) continue; if (CheckLine(temp,PLAYER[0],SIGHT) && (Near(temp,PLAYER[0],4) || MISCVARS->madenoise)) { dx = PLAYER[0]->x - temp->x; dy = temp->y - PLAYER[0]->y; destdir = (angletodir[atan2_appx(dx,dy)] << 1); temp->temp1 = destdir; temp->targettilex = temp->dir; NewState(temp,align); temp->temp3 = ob->temp3; } else NewState(temp,wait); } return; } SD_PlaySoundRTP(SD_ROBOTMOVESND,ob->x,ob->y); SelectPathDir(ob); } void A_Shoot (objtype *ob) { int dx,dy,dz,dist; int accuracy,damage,sound; objtype * target; int num; int savedangle; ActorMovement(ob); //ob->flags |= FL_FULLLIGHT; bats don't emit light //if (!(ob->flags & FL_SHOOTABLE)) //Error("\na dead instance of %s is shooting at you",debugstr[ob->obclass]); if (!ob->ticcount) { if (ob->obclass == strikeguardobj) ob->flags &= ~FL_NOFRICTION; target = (objtype*)(ob->target); if (!target) Error("an instance of %s called shoot without a target\n",debugstr[ob->obclass]); if(!(ob->obclass == blitzguardobj && ob->temp3 == stat_bat)) { ob->flags &= ~FL_FULLLIGHT; } dx = (target->x - ob->x); dy = (ob->y - target->y); dz = target->z-ob->z; if(ob->obclass == blitzguardobj && ob->temp3 == stat_bat ) { //is the target close enough for me to hit with my bat? if ((abs(dx) <= 0x10000) && (abs(dy) <= 0x10000) && (abs(dz) <= 20)) BlitzBatAttack(ob, target); else //resort to pistol to damage target goto pistol; ob->target = NULL; return; } else if ((ob->obclass == blitzguardobj) && (ob->temp3) && (ob->temp3 != stat_gasmask) && (ob->temp3 != stat_asbesto) && (ob->temp3 != stat_bulletproof) && (gamestate.difficulty >= gd_medium) && ((abs(dx) > 0xc000) || (abs(dy) > 0xc000)) ) { int i; missile_stats* newmissiledata; newmissiledata = &PlayerMissileData[GetWeaponForItem(ob->temp3)]; // ready to annihilate this poor bastard SpawnMissile(ob,newmissiledata->obclass,newmissiledata->speed, AngleBetween(ob,player), newmissiledata->state, newmissiledata->offset); if (newmissiledata->obclass == p_drunkmissileobj) { for(i=0; i<4; i++) { SpawnMissile(ob,newmissiledata->obclass,newmissiledata->speed, AngleBetween(ob,player), newmissiledata->state, newmissiledata->offset); } } ob->target = NULL; ob->temp2 --; if (ob->temp2 == 0) ob->temp3 = 0; return; } if ((!areabyplayer[ob->areanumber]) && (target->obclass == playerobj)) return; //if (!CheckLine(ob,target,SHOOT)) // player is behind a wall //return; pistol: savedangle=ob->angle; ob->angle = atan2_appx (dx,dy); dist = FindDistance(dx,dy); ob->yzangle = FINEANGLES-atan2_appx(dist, dz<<10); if ((ob->yzangle>MAXYZANGLE) && (ob->yzangle<FINEANGLES-MAXYZANGLE)) ob->yzangle=MAXYZANGLE; dist>>=16; accuracy=(WHICHACTOR<<4)+((gamestate.difficulty) << 6); num = GameRandomNumber("A_Shoot3",ob->obclass); if (num<128) num=128; // Don't let accuracy fall below 50% original accuracy=FixedMulShift(num,accuracy,8); // scale accuracy based off randomness // check for maximum accuracy; if (accuracy>255) accuracy=255; if (ob->obclass==highguardobj) damage=DMG_MP40; else if (ob->obclass == triadenforcerobj) damage=DMG_MP40; else damage=DMG_ENEMYBULLETWEAPON; RayShoot (ob, damage, 255-accuracy); ob->angle=savedangle; sound = BAS[ob->obclass].fire; SD_PlaySoundRTP(sound,ob->x,ob->y); MISCVARS->madenoise = true; if ((!(ob->flags& FL_HASAUTO)) || (!ob->temp3)) ob->target = NULL; } } /* =============== = = A_Shoot = = Try to damage the player, based on skill level and player's speed = =============== */ /* void A_Shoot (objtype *ob) { int dx,dy,dz,dist; int accuracy,damage,sound; objtype * target; int num; int savedangle; ActorMovement(ob); ob->flags |= FL_FULLLIGHT; //if (!(ob->flags & FL_SHOOTABLE)) //Error("\na dead instance of %s is shooting at you",debugstr[ob->obclass]); if (!ob->ticcount) { if (ob->obclass == strikeguardobj) ob->flags &= ~FL_NOFRICTION; target = (objtype*)(ob->target); if (!target) Error("an instance of %s called shoot without a target\n",debugstr[ob->obclass]); ob->flags &= ~FL_FULLLIGHT; dx = (target->x - ob->x); dy = (ob->y - target->y); dz = target->z-ob->z; if ((ob->obclass == blitzguardobj) && (ob->temp3) && (ob->temp3 != stat_gasmask) && (ob->temp3 != stat_asbesto) && (ob->temp3 != stat_bulletproof) && (gamestate.difficulty >= gd_medium) && ((abs(dx) > 0xc000) || (abs(dy) > 0xc000)) ) { int i; missile_stats* newmissiledata; newmissiledata = &PlayerMissileData[GetWeaponForItem(ob->temp3)]; // ready to annihilate this poor bastard SpawnMissile(ob,newmissiledata->obclass,newmissiledata->speed, AngleBetween(ob,player), newmissiledata->state, newmissiledata->offset); if (newmissiledata->obclass == p_drunkmissileobj) { for(i=0; i<4; i++) { SpawnMissile(ob,newmissiledata->obclass,newmissiledata->speed, AngleBetween(ob,player), newmissiledata->state, newmissiledata->offset); } } ob->target = NULL; ob->temp2 --; if (ob->temp2 == 0) ob->temp3 = 0; return; } if ((!areabyplayer[ob->areanumber]) && (target->obclass == playerobj)) return; //if (!CheckLine(ob,target,SHOOT)) // player is behind a wall //return; savedangle=ob->angle; ob->angle = atan2_appx (dx,dy); dist = FindDistance(dx,dy); ob->yzangle = FINEANGLES-atan2_appx(dist, dz<<10); if ((ob->yzangle>MAXYZANGLE) && (ob->yzangle<FINEANGLES-MAXYZANGLE)) ob->yzangle=MAXYZANGLE; dist>>=16; accuracy=(WHICHACTOR<<4)+((gamestate.difficulty) << 6); num = GameRandomNumber("A_Shoot3",ob->obclass); if (num<128) num=128; // Don't let accuracy fall below 50% original accuracy=FixedMulShift(num,accuracy,8); // scale accuracy based off randomness // check for maximum accuracy; if (accuracy>255) accuracy=255; if (ob->obclass==highguardobj) damage=DMG_MP40; else if (ob->obclass == triadenforcerobj) damage=DMG_MP40; else damage=DMG_ENEMYBULLETWEAPON; RayShoot (ob, damage, 255-accuracy); ob->angle=savedangle; sound = BAS[ob->obclass].fire; SD_PlaySoundRTP(sound,ob->x,ob->y); MISCVARS->madenoise = true; if ((!(ob->flags& FL_HASAUTO)) || (!ob->temp3)) ob->target = NULL; } } */ void A_Repeat(objtype*ob) { ActorMovement(ob); if (!ob->ticcount) { ob->temp3 --; if (ob->temp3 <= 0) NewState(ob,M_S(CHASE)); } } void A_MissileWeapon(objtype *ob) { int sound,nspeed,noffset,zoffset; #if (SHAREWARE == 0) int oldyzangle; #endif classtype nobclass; statetype*nstate; if ((ob->obclass == wallopobj) || (ob->obclass == roboguardobj)); //SelectPathDir(ob); else ActorMovement(ob); if (!ob->ticcount) { #if (SHAREWARE == 0) if ((ob->obclass == wallopobj) && (!ob->temp3)) { NewState(ob,&s_wallrestore); return; } #endif if ((ob->obclass == b_darianobj) && (!CheckLine(ob,PLAYER[0],SHOOT))) { NewState(ob,M_S(CHASE)); return; } // Move sounds, flags into switch cases sound = BAS[ob->obclass].fire; nspeed = 0x4000; noffset = 0x8000; zoffset = 0; switch (ob->obclass) { case triadenforcerobj: nobclass = grenadeobj; nstate= &s_grenade1; sound++; break; case roboguardobj: nobclass = shurikenobj; nspeed = 0x2000; noffset = 0x10000; nstate = &s_robogrdshuriken1; break; /* case b_darkmonkobj: nobclass = dmfballobj; nstate = &s_dmfball1; break; */ /* case b_robobossobj: nobclass = bigshurikenobj; nstate = &s_oshuriken1; break; */ #if (SHAREWARE == 0) case b_darianobj: nobclass = missileobj; //nspeed = 0x100; //noffset = 0x18000; nstate = &s_missile1; zoffset = -20; break; case dfiremonkobj: nobclass = fireballobj; nstate = &s_monkfire1; break; case overpatrolobj: nobclass = netobj; nstate = &s_bolocast1; sound ++; break; case wallopobj: { int dx,dy,dz,xydist; ob->temp2 ^= 1; // increment numfired ob->temp3 --; // decrement random fire no. dx = PLAYER[0]->x-ob->x; dy = ob->y-PLAYER[0]->y; if (GameRandomNumber("bcraft shoot up/down",0) < 128) dz = 10; else dz = -10; xydist = FindDistance(dx,dy); oldyzangle = ob->yzangle; ob->yzangle = atan2_appx(xydist,dz<<10); if (ob->temp2) { nobclass = missileobj; nstate = &s_missile1; } else { nobclass = bigshurikenobj; nstate = &s_bstar1; } } break; #endif default: ; } SpawnMissile(ob,nobclass,nspeed,AngleBetween(ob,PLAYER[0]),nstate,noffset); new->z += zoffset; SD_PlaySoundRTP(sound,ob->x,ob->y); MISCVARS->madenoise = true; if (ob->obclass == triadenforcerobj) { //new->flags |= (FL_SHOOTABLE); new->temp1 = 0x50000; } #if (SHAREWARE == 0) else if (ob->obclass == wallopobj) ob->yzangle = oldyzangle; #endif } } void A_Wallfire(objtype *ob) { if (!(ob->flags & FL_ACTIVE)) return; if (!ob->ticcount) { SpawnMissile(ob,wallfireobj,0x4000,ob->angle,&s_crossfire1,0xa000); if (areabyplayer[ob->areanumber]) SD_PlaySoundRTP(SD_FIRECHUTESND,ob->x,ob->y); new->dir = angletodir[new->angle]; new->z = nominalheight; } } void T_Reset(objtype *ob) { ActorMovement(ob); if (ob->ticcount) return; ob->momentumx = ob->momentumy = ob->dirchoosetime = 0; ob->flags &= ~FL_NOFRICTION; } void SelectRollDir(objtype *ob) { int angle,tryx,tryy; if (ob->state == &s_strikerollright1) angle = AngleBetween(ob,PLAYER[0]) + ANGLES/4; else angle = AngleBetween(ob,PLAYER[0]) - ANGLES/4; Fix(angle); tryx = ob->x + FixedMul(0x20000l,costable[angle]); tryy = ob->y - FixedMul(0x20000l,sintable[angle]); ZEROMOM; if (QuickSpaceCheck(ob,tryx,tryy)) { int oldspeed; oldspeed = ob->speed; ob->speed = ROLLMOMENTUM; ParseMomentum(ob,angle); ob->speed = oldspeed; ob->dirchoosetime = 5; ob->flags |= FL_NOFRICTION; } else ob->dirchoosetime = 0; } void SelectDodgeDir (objtype *ob) { int dx,dy,i,tx,ty; unsigned absdx,absdy; dirtype dirtry[5]; dirtype turnaround,tdir,olddir; olddir = ob->dir; if (ob->flags & FL_FIRSTATTACK) { // // turning around is only ok the very first time after noticing the // player // turnaround = nodir; ob->flags &= ~FL_FIRSTATTACK; } else turnaround=opposite[ob->dir]; if (ob->targettilex || ob->targettiley) { tx = ob->targettilex; ty = ob->targettiley; dx= tx - ob->x; dy= ty - ob->y; if ( ((dx < MINACTORDIST) && (dx > -MINACTORDIST)) && ((dy < MINACTORDIST) && (dy > -MINACTORDIST))) { dx= PLAYER[0]->x-ob->x; dy= PLAYER[0]->y-ob->y; } } else { dx= PLAYER[0]->x-ob->x; dy= PLAYER[0]->y-ob->y; } // // arange 5 direction choices in order of preference // the four cardinal directions plus the diagonal straight towards // the player // if (dx>ACTORSIZE) { dirtry[1]= east; dirtry[3]= west; } else if (dx < -ACTORSIZE) { dirtry[1]= west; dirtry[3]= east; } if (dy>ACTORSIZE) { dirtry[2]= south; // south dirtry[4]= north; // north } else if (dy <-ACTORSIZE) { dirtry[2]= north; // north dirtry[4]= south; // south } // // randomize a bit for dodging // absdx = abs(dx); absdy = abs(dy); if (absdx > absdy) { tdir = dirtry[1]; dirtry[1] = dirtry[2]; dirtry[2] = tdir; tdir = dirtry[3]; dirtry[3] = dirtry[4]; dirtry[4] = tdir; } if (GameRandomNumber("SelectDogeDir",ob->obclass) < 128) { tdir = dirtry[1]; dirtry[1] = dirtry[2]; dirtry[2] = tdir; tdir = dirtry[3]; dirtry[3] = dirtry[4]; dirtry[4] = tdir; } dirtry[0] = diagonal [dirtry[1]] [dirtry[2]]; ZEROMOM; for (i=0; i<5; i++) { if ((dirtry[i] == nodir) || (dirdiff[dirtry[i]][olddir] > 1)) continue; M_CHECKDIR(ob,dirtry[i]); } // // turn around only as a last resort // // for(tdir = east;tdir<=southeast;tdir++) //if (tdir != turnaround) // M_CHECKDIR(ob,tdir); if (turnaround != nodir) M_CHECKDIR(ob,turnaround); } #define TryAbruptProximalDirections(trydir) \ { \ next = dirorder[trydir][NEXT]; \ prev = dirorder[trydir][PREV]; \ if (GameRandomNumber("actor choose dir",0) < 128) \ { \ dirtype temp = next; \ \ next = prev; \ prev = temp; \ } \ \ if (!dirtried[next]) \ { \ M_CHECKDIR(ob,next); \ dirtried[next]=1; \ } \ \ if (!dirtried[prev]) \ { \ M_CHECKDIR(ob,prev); \ dirtried[prev]=1; \ } \ \ } #define TrySmoothProximalDirections(trydir) \ { \ \ if (((trydir == olddir) || (dirdiff[trydir][olddir] < 2)) && \ (!dirtried[trydir])) \ { \ M_CHECKDIR(ob,trydir); \ dirtried[trydir] = 1; \ } \ next = dirorder[olddir][NEXT]; \ prev = dirorder[olddir][PREV]; \ \ if (dirdiff[trydir][next] <= dirdiff[trydir][prev]) \ { \ start = next; \ whichway = NEXT; \ } \ else \ { \ start = prev; \ whichway = PREV; \ } \ \ for (tdir= start; tdir != dirorder[trydir][whichway]; \ tdir = dirorder[tdir][whichway] \ ) \ { \ if (dirtried[tdir]) \ continue; \ M_CHECKDIR(ob,tdir); \ dirtried[tdir]=1; \ } \ \ } #define ChasePlayer(ob) \ { \ dx= player->x-ob->x; \ dy= ob->y-player->y; \ if ((abs(dx) < 0xb000) && (abs(dy) < 0xb000)) \ return; \ dummy.x = player->x; \ dummy.y = player->y; \ } void SelectChaseDir (objtype *ob) { int dx,dy,whichway,tx,ty,actrad,visible, realdiff; dirtype dtry1,dtry2,tdir,olddir,next,prev,start,straight; tpoint dummy; byte dirtried[9] = {0}; olddir=ob->dir; visible = CheckLine(ob,PLAYER[0],SIGHT); /* if (ob->flags & FL_FIRSTATTACK) { // // turning around is only ok the very first time after noticing the // player // turnaround = opposite[ob->dir]; ob->flags &= ~FL_FIRSTATTACK; } else turnaround=nodir; */ if (ob->targettilex || ob->targettiley) { tx = ob->targettilex; ty = ob->targettiley; dx= tx - ob->x; dy= ob->y - ty; if ((abs(dx) < 0x2000) && (abs(dy) < 0x2000)) ChasePlayer(ob); } else ChasePlayer(ob); //if ((abs(dx) < 0x10000) && (abs(dy) < 0x10000)) //return; straight = angletodir[atan2_appx(dx,dy)]; realdiff = dirdiff[straight][ob->dir]; ZEROMOM; //insertion 20 actrad = ACTORSIZE; dtry1=nodir; dtry2=nodir; if (dx> actrad) dtry1= east; else if (dx< -actrad) dtry1= west; if (dy> actrad) dtry2=north; else if (dy < -actrad) dtry2= south; if (abs(dy)>abs(dx)) { tdir=dtry1; dtry1=dtry2; dtry2=tdir; } if (GameRandomNumber("chase minor",0) < 80) { tdir=dtry1; dtry1=dtry2; dtry2=tdir; } ZEROMOM; if ((!visible) || (realdiff > 2)) // don't worry about abrupt or unrealistic turns if player // can't see guards { M_CHECKDIR(ob,straight); dirtried[straight]=1; next = dirorder[straight][NEXT]; prev = dirorder[straight][PREV]; if ((dtry1 != nodir) && (dtry1 != straight)) { M_CHECKDIR(ob,dtry1); dirtried[dtry1]=1; } if ((dtry2 != nodir) && (!dirtried[dtry2])) { M_CHECKDIR(ob,dtry2); dirtried[dtry2]=1; } if (dtry1 != nodir) TryAbruptProximalDirections(dtry1); if (dtry2 != nodir) TryAbruptProximalDirections(dtry2); } else { if (realdiff < 2) { M_CHECKDIR(ob,straight); dirtried[straight]=1; } if (dtry1 != nodir) TrySmoothProximalDirections(dtry1); if (dtry2 != nodir) TrySmoothProximalDirections(dtry2); } if ((dtry1!=nodir) || (dtry2!=nodir)) { if (GameRandomNumber("actor choose dir",0) < 128) whichway = NEXT; else whichway = PREV; for(tdir = dirorder[olddir][whichway]; tdir != olddir; tdir = dirorder[tdir][whichway]) { if (dirtried[tdir]) continue; M_CHECKDIR(ob,tdir); } } ob->dir = olddir; } int Near(objtype *ob,void* what,int distance) { objtype *aw; aw = (objtype*) what; if (FindDistance((aw->x - ob->x),(aw->y - ob->y)) <= (distance<<16)) return 1; return 0; } /* =============== = = SelectPathDir = =============== */ void SelectPathDir (objtype *ob) { int spot,centerx,centery,dx,dy,set,done,radius,ocl; ocl = ob->obclass; if ((ocl == bladeobj) && (!(ob->flags & FL_ACTIVE))) return; spot = MAPSPOT(ob->tilex,ob->tiley,1)-ICONARROWS; set = ((ocl == wallopobj) || (ocl == roboguardobj)); done = (((!set) && (ob->dir == (dirtype)spot)) || (set && (ob->dir == (dirtype)(spot<<1)))); if ((spot >= 0) && (spot<= 7) && (!done)) { centerx= (ob->tilex << 16) + HALFGLOBAL1; centery= (ob->tiley << 16) + HALFGLOBAL1; dx = abs(centerx - ob->x); dy = abs(centery - ob->y); //radius = (ob->speed > 0x4800)?(0xb000):(0x4000); radius = 0x4000; if ((dx < radius) && (dy < radius)) // new direction { ZEROMOM; if ((ocl == wallopobj) || (ocl == roboguardobj)) { ob->dir = spot<<1; ParseMomentum(ob,dirangle16[ob->dir]); } else { ob->dir = spot; ParseMomentum(ob,dirangle8[ob->dir]); } dx = centerx - ob->x; dy = centery - ob->y; SetFinePosition(ob,centerx,centery); SetVisiblePosition(ob,centerx,centery); /* if (((ocl == bladeobj) || (ocl == diskobj)) && ob->whatever) {objtype*passenger = (objtype*)(ob->whatever); passenger->x += dx; passenger->y += dy; passenger->drawx = passenger->x; passenger->drawy = passenger->y; passenger->tilex = passenger->x >> 16; passenger->tiley = passenger->y >> 16; }*/ // if (ob==SNAKEHEAD) // Debug("\n path changed at %d, %d", // ob->tilex,ob->tiley); } } if (NOMOM) { if ((ocl == wallopobj) || (ocl == roboguardobj)) ParseMomentum(ob,dirangle16[ob->dir]); else ParseMomentum(ob,dirangle8[ob->dir]); } //if ((ob->obclass == firejetobj) || (ob->obclass == bladeobj)) //MoveActor(ob); //else ActorMovement(ob); } /* ================ = = CheckSight = = Checks a straight line between player and current object = = If the sight is ok, check alertness and angle to see if they notice = = returns true if the player has been spoted = ================ */ boolean CheckSight (objtype *ob,void *atwhat) { long deltax,deltay; objtype * what; // // don't bother tracing a line if the area isn't connected to the player's // if (!areabyplayer[ob->areanumber]) return false; // // if the player is real close, sight is automatic // what = (objtype*)atwhat; deltax = what->x - ob->x; deltay = what->y - ob->y; if ((deltax > -MINSIGHT) && (deltax < MINSIGHT) && (deltay > -MINSIGHT) && (deltay < MINSIGHT)) return true; // // see if they are looking in the right direction // switch (ob->dir) { case north: if (deltay > 0) return false; break; case east: if (deltax < 0) return false; break; case south: if (deltay < 0) return false; break; case west: if (deltax > 0) return false; break; default: ; } // // trace a line to check for blocking tiles (corners) // return CheckLine (ob,atwhat,SIGHT); } void ActivateEnemy(objtype*ob) { statetype *temp; ob->flags |= (FL_ATTACKMODE|FL_FIRSTATTACK); if (ob->obclass == roboguardobj) return; if (ob->temp3 == SNEAKY) { NewState(ob,&s_sneakyrise1); ob->temp3=0; } else if ((temp = M_S(CHASE)) != NULL) NewState(ob,temp); /* ob->speed = ENEMYRUNSPEED; */ if (ob->obclass == b_heinrichobj) ob->speed = 7*ob->speed/2; else if (ob->obclass == b_darianobj) ob->speed = 3*SPDPATROL; if (ob->door_to_open != -1) ob->door_to_open = -1; // ignore the door opening command ob->dirchoosetime = 0; } /* =============== = = FirstSighting = = Puts an actor into attack mode and possibly reverses the direction = if the player is behind it = =============== */ void FirstSighting (objtype *ob) { statetype *temp; int sound; if (ob->temp3 == SNEAKY) { NewState(ob,&s_sneakyrise1); ob->temp3=0; if (ob->shapeoffset==0) SD_PlaySoundRTP(SD_SNEAKYSPRINGMSND,ob->x,ob->y); else SD_PlaySoundRTP(SD_SNEAKYSPRINGFSND,ob->x,ob->y); } else if ((temp = M_S(CHASE)) != NULL) { int rand; NewState(ob,temp); sound = BAS[ob->obclass].see; rand = GameRandomNumber("FirstSighting low",0); if ((ob->obclass > lowguardobj) && (ob->obclass <= blitzguardobj) && (rand < 128)) //hack for alternate sound++; //if ((ob->obclass == lowguardobj) && (rand < 80)) //sound ++; else if (ob->obclass == lowguardobj) { if (rand < 128) { if ((PLAYERSTATE[0].player == 1) || (PLAYERSTATE[0].player == 3)) sound++; } else sound += 2; if (ob->shapeoffset) sound += 4; } SD_PlaySoundRTP(sound,ob->x,ob->y); if ((ob->obclass>=b_darianobj) && (ob->obclass<=b_darksnakeobj)) { MU_StartSong(song_bosssee); } } /* ob->speed = ENEMYRUNSPEED;*/ if (ob->obclass == b_heinrichobj) ob->speed = 7*ob->speed/2; else if (ob->obclass == b_darianobj) ob->speed = 3*SPDPATROL; if (ob->door_to_open != -1) ob->door_to_open = -1; // ignore the door opening command ob->dirchoosetime = 0; ob->flags |= (FL_ATTACKMODE|FL_FIRSTATTACK); } /* =============== = = SightPlayer = = Called by actors that ARE NOT chasing the player. If the player = is detected (by sight, noise, or proximity), the actor is put into = it's combat frame and true is returned. = = Incorporates a random reaction delay = =============== */ boolean SightPlayer (objtype *ob) { //if (ob->flags & FL_ATTACKMODE) //Error ("An instance of %s in ATTACKMODE called SightPlayer!",debugstr[ob->obclass]); if (!areabyplayer[ob->areanumber]) return false; if (ob->obclass == b_robobossobj) { if (!(CheckSight(ob,player) || Near(ob,player,6))) return false; } else if (ob->flags & FL_AMBUSH) { if (!CheckSight (ob,PLAYER[0])) { //SoftError("\n failed from ambush in SightPlayer"); return false; } ob->flags &= ~FL_AMBUSH; } else { if (ob->temp3 == SNEAKY) { if (!Near(ob,PLAYER[0],2)) return false; } else if (!((MISCVARS->madenoise) || (CheckSight (ob,player)) || (Near(ob,player,4)) ) ) { //SoftError("\n failed from SightPlayer"); return false; } } FirstSighting (ob); return true; } /* ===================== = = CheckLine = = Returns true if a straight line between two obs is unobstructed = ===================== */ boolean CheckLine (void *from, void *to, int condition) { objtype *tempactor,*ob,*orig; statobj_t *checksprite; int destx,desty,destz; int desttilex,desttiley; int snx,sny; int incr[2]; int thedir[2]; int cnt; int grid[2]; int index; int vx,vy; int yzangle; int value; int dx,dy,dz; int xydist; int otx,oty,count=0; ob = (objtype*)to; orig = (objtype*)from; if (ob->which == SPRITE) { destx = ((statobj_t*)to)->x; desty = ((statobj_t*)to)->y; destz = ((statobj_t*)to)->z; } else { destx = ob->x; desty = ob->y; destz = ob->z; } desttilex=destx>>16; desttiley=desty>>16; if ((desttilex == orig->tilex) && (desttiley == orig->tiley)) return true; dx=destx-orig->x; dy=orig->y-desty; dz=orig->z-destz; xydist = FindDistance(dx,dy); yzangle = atan2_appx(xydist,dz<<10); if ((yzangle>MAXYZANGLE) && (yzangle<FINEANGLES-MAXYZANGLE)) { return false; } //angle = atan2_appx(dx,dy); otx = orig->x >> TILESHIFT; oty = orig->y >> TILESHIFT; if (xydist==0) { /* SoftError("\nCheckLine:xydist=0"); if (orig->which == ACTOR) SoftError("shooter: %s",debugstr[orig->obclass]); if (ob->which == ACTOR) SoftError("target: %s",debugstr[ob->obclass]);*/ vy=-dy; vx=dx; } else { vy = -FixedDiv2(dy,xydist); vx = FixedDiv2(dx,xydist); } snx=orig->x&0xffff; sny=orig->y&0xffff; grid[0]=otx; grid[1]=oty; if (vx>0) { thedir[0]=1; snx^=0xffff; incr[1]=-vx; } else { thedir[0]=-1; incr[1]=vx; } if (vy>0) { thedir[1]=1; sny^=0xffff; incr[0]=vy; } else { thedir[1]=-1; incr[0]=-vy; } cnt=FixedMul(snx,incr[0])+FixedMul(sny,incr[1]); do { count ++; /* if (count > 1000) Error("possible infinite loop in CheckLine"); if ((grid[0] < 0) || (grid[0] > (MAPSIZE-1)) || (grid[1] < 0) || (grid[1] > (MAPSIZE-1))) Error("out of bounds in check line, grid[0] = %d, grid[1] = %d",grid[0],grid[1]); */ if ((grid[0]==desttilex) && (grid[1]==desttiley)) return true; tempactor = (objtype*)actorat[grid[0]][grid[1]]; value = tilemap[grid[0]][grid[1]]; checksprite = sprites[grid[0]][grid[1]]; if (value) { if (value&0x8000) { if (!(value&0x4000)) { doorobj_t*dptr = doorobjlist[value&0x3ff]; if (dptr->position < 0x8000) { int x = (grid[0] << 16) + 0x8000; int y = (grid[1] << 16) + 0x8000; if (dptr->vertical) { if (abs(dx) > abs(x-orig->x)) return false; } else { if (abs(dy) > abs(orig->y-y)) return false; } } } else { if (condition == SHOOT) { if ( maskobjlist[value&0x3ff]->flags & MW_SHOOTABLE ) { return false; } else if ( maskobjlist[value&0x3ff]->flags & MW_WEAPONBLOCKING ) { return false; } } else if ((condition == MISSILE) && ( maskobjlist[value&0x3ff]->flags & MW_BLOCKING ) ) return false; } } else { return false; } } if (condition == SHOOT) { if (tempactor && (tempactor->which == ACTOR) && (tempactor->flags & FL_BLOCK) && (tempactor != orig) && (tempactor != ob)) //&& // (InRange(orig,tempactor, // FindDistance(orig->x-tempactor->x,orig->y-tempactor->y) ) // ==true) ) { return false; } } if (checksprite && (checksprite->flags & FL_BLOCK) && (condition == SHOOT) && ((void *)checksprite != to) && (checksprite->itemnumber!=stat_disk) && (InRange(orig,(objtype *)checksprite, FindDistance(orig->x-checksprite->x,orig->y-checksprite->y) ) ==true) ) { return false; } if (tempactor && (tempactor->which == PWALL)) { return false; } index=(cnt>=0); cnt+=incr[index]; grid[index]+=thedir[index]; } while (1); } /* ===================== = = ShootActor = = Shoot an actor. = ===================== */ void ShootActor(objtype * shooter, objtype * target, int damage, int accuracy, int angle) { int dx,dy,dist; int newmomx, newmomy; int tcl; if (target->flags & FL_DYING) return; dx = abs(shooter->x - target->x); dy = abs(shooter->y - target->y); dist = FindDistance(dx,dy)>>16; tcl=target->obclass; if (tcl==playerobj) { target->target=shooter; if (target->flags&FL_BPV) { playertype *pstate; M_LINKSTATE(target,pstate); pstate->protectiontime -= (damage<<1); if (pstate->protectiontime < 1) pstate->protectiontime = 1; if (target==player) GM_UpdateBonus (pstate->protectiontime, false); return; } else if ((target->flags&FL_GODMODE) || (target->flags&FL_DOGMODE) || godmode) return; //damage=FixedMulShift((gamestate.difficulty+1),damage,2); // player object difficulty } else if (tcl == NMEsaucerobj) { target->momentumx = target->momentumy = 0; NewState(target,&s_explosion1); target->flags &= ~FL_SHOOTABLE; return; } else if (tcl == b_darianobj) MISCVARS->ESAU_SHOOTING = false; else if ((tcl == strikeguardobj) || (tcl == b_heinrichobj)) target->target = shooter; if (( (!(target->flags & FL_SHOOTABLE)) || (tcl == roboguardobj) || (tcl == wallopobj) || (tcl == patrolgunobj) ) && (tcl!=playerobj) ) SpawnMetalSparks(target,angle); else if ((tcl < b_darianobj) || (tcl > b_darksnakeobj)) { //target->flags &= ~FL_USE; damage=FixedMulShift(511-accuracy,damage,9); // Min half damage if (dist<64) { if (dist>2) damage=FixedMulShift(63-dist,damage,6); if (damage<1) damage=1; } else damage=1; if (damage>MAXDAMAGE) damage=MAXDAMAGE; // absolutely clip it DamageThing(target,damage); if ((tcl == collectorobj) && gamestate.SpawnDeluder) { Collision(target,shooter,0,0); if (target->hitpoints <= 0) BATTLE_CheckGameStatus(battle_shot_deluder,shooter->dirchoosetime); } else { newmomx = FixedMul(damage<<7,costable[angle]); newmomy = -FixedMul(damage<<7,sintable[angle]); Collision(target,shooter,-(target->momentumx)+newmomx,-(target->momentumy)+newmomy); if (tcl == playerobj) { playertype * pstate; M_LINKSTATE(target,pstate); if (pstate->health <= 0) { if (shooter->obclass == playerobj) { if (!target->momentumz) BATTLE_PlayerKilledPlayer(battle_kill_with_bullet,shooter->dirchoosetime,target->dirchoosetime); else BATTLE_PlayerKilledPlayer(battle_kill_with_bullet_in_air,shooter->dirchoosetime,target->dirchoosetime); } } } // SoftError("ShootActor: damage=%ld dist=%ld\n",damage,dist); if ((GameRandomNumber("disembowel",0)<64) && (gamestate.violence == vl_excessive)) { int temp; temp=target->temp1; target->temp1=angle; SpawnParticles(target,DISEMBOWEL,damage>>3); target->temp1=temp; } else if (gamestate.violence > 0) SpawnBlood(target,angle); } } } /* ===================== = = ShootSprite = = Shoot a sprite. = ===================== */ void ShootSprite(objtype * shooter, statobj_t * target, int damage, int accuracy, int angle) { int dx,dy,dist; if (!(target->flags & FL_SHOOTABLE)) // Watchout for sprite being passed in as actor WARNING SpawnMetalSparks((objtype *)target,angle); else { dx = abs(shooter->x - target->x); dy = abs(shooter->y - target->y); dist = FindDistance(dx,dy)>>16; damage=FixedMulShift(511-accuracy,damage,9); // Min half damage if (dist<64) { if (dist>2) damage=FixedMulShift(63-dist,damage,6); if (damage<1) damage=1; } else damage=1; if (damage>MAXDAMAGE) damage=MAXDAMAGE; // absolutely clip it // SoftError("ShootSprite: damage=%ld dist=%ld\n",damage,dist); DamageThing((objtype *)target,damage); if (FirstExplosionState(new->state)) new->whatever = shooter; SpawnStaticDamage(target, angle); } } /* ===================== = = RayShoot = = Cast a ray out at the shooter's angle and yzangle, return = ===================== */ void RayShoot (objtype * shooter, int damage, int accuracy) { objtype *tempactor; statobj_t *checksprite; int snx,sny; int incr[2]; int zincr[2]; int thedir[2]; int cnt; int grid[2]; int index; int vx,vy; int angle; int yzangle; int value; int offset; int z; int lastcnt; int bullethole=0; enum {gs_door, gs_wall, gs_floor, gs_ceiling, gs_pushwall}; int smokecondition=0; if ((shooter->areanumber==player->areanumber) && (Near(shooter,player,3))) SetIllumination(2); offset = ((GameRandomNumber("RayShoot",0)-128)>>MAXSHOOTSHIFT); offset = FixedMulShift(accuracy,offset,8); if (offset>MAXSHOOTOFFSET) offset=MAXSHOOTOFFSET; else if (offset<-MAXSHOOTOFFSET) offset=-MAXSHOOTOFFSET; angle=(shooter->angle+offset)&(FINEANGLES-1); offset = ((GameRandomNumber("RayShoot",1)-128)>>MAXSHOOTSHIFT); offset = FixedMulShift(accuracy,offset,8); if (offset>MAXSHOOTOFFSET) offset=MAXSHOOTOFFSET; else if (offset<-MAXSHOOTOFFSET) offset=-MAXSHOOTOFFSET; yzangle=(shooter->yzangle+offset)&(FINEANGLES-1); vy = -sintable[angle]; vx = costable[angle]; snx=shooter->x&0xffff; sny=shooter->y&0xffff; grid[0]=shooter->tilex; grid[1]=shooter->tiley; if (shooter->obclass==playerobj) { playertype * pstate; M_LINKSTATE(shooter,pstate); z=shooter->z+pstate->playerheight-32; } else z=shooter->z-7; if (vx>0) { thedir[0]=1; snx^=0xffff; incr[1]=-vx; } else { thedir[0]=-1; incr[1]=vx; } if (vy>0) { thedir[1]=1; sny^=0xffff; incr[0]=vy; } else { thedir[1]=-1; incr[0]=-vy; } zincr[0]=-FixedMulShift(sintable[yzangle],abs(vx),26); zincr[1]=-FixedMulShift(sintable[yzangle],abs(vy),26); cnt=FixedMul(snx,incr[0])+FixedMul(sny,incr[1]); index= (cnt >= 0); do { tempactor = (objtype*)actorat[grid[0]][grid[1]]; value = tilemap[grid[0]][grid[1]]; checksprite = sprites[grid[0]][grid[1]]; if (value) { if (value&0x8000) { if (!(value&0x4000)) { if ((doorobjlist[value&0x3ff]->action==dr_closed) || (z<maxheight-64)) { smokecondition=gs_door; break; } else if (doorobjlist[value&0x3ff]->position<=0x8000) { smokecondition=gs_door; break; } } else { if ( maskobjlist[value&0x3ff]->flags & MW_SHOOTABLE ) { if (z>maxheight-64) // Are we shooting above the glass { UpdateMaskedWall(value&0x3ff); return; } } else if ( maskobjlist[value&0x3ff]->flags & MW_WEAPONBLOCKING ) { smokecondition=gs_door; break; } } } else { smokecondition=gs_wall; break; } } if (checksprite && ((checksprite->flags & FL_BLOCK)||(checksprite->flags & FL_SHOOTABLE)) && (abs(checksprite->z-z)<32) && (InRange(shooter,(objtype *)checksprite, FindDistance(shooter->x-checksprite->x,shooter->y-checksprite->y) ) ==true ) ) { ShootSprite(shooter, checksprite, damage, accuracy, angle); return; } if (tempactor) { if (tempactor->which == ACTOR) { if ((abs(tempactor->z-z)<32 ) && (!(tempactor->flags & FL_DYING)) && (tempactor->flags & FL_BLOCK) && (tempactor != shooter) && (tempactor->obclass!=diskobj) && (InRange(shooter,tempactor, FindDistance(shooter->x-tempactor->x,shooter->y-tempactor->y) ) ==true ) ) { ShootActor(shooter, tempactor, damage, accuracy, angle); return; } } else if (tempactor->which == PWALL) return; } if (z<-32) { smokecondition=gs_ceiling; break; } else if (z>maxheight) { smokecondition=gs_floor; break; } index= (cnt >= 0); cnt+=incr[index]; z +=zincr[index]; grid[index]+=thedir[index]; } while (1); if (IsWindow(grid[0],grid[1])) return; lastcnt=cnt-incr[index]; if (smokecondition==gs_floor) { int dist; int tangentangle; tangentangle=tantable[yzangle]; if (tangentangle!=0) { dist=FixedDiv2(((shooter->z-maxheight)<<10),(tangentangle<<1)); xintercept=shooter->x+FixedMul(dist,costable[angle]); yintercept=shooter->y-FixedMul(dist,sintable[angle]); } z=maxheight; // bullethole=5; } else if (smokecondition==gs_ceiling) { int dist; int tangentangle; if (sky!=0) return; tangentangle=tantable[yzangle]; if (tangentangle!=0) { dist=FixedDiv2(((shooter->z+32)<<10),(tangentangle<<1)); xintercept=shooter->x+FixedMul(dist,costable[angle]); yintercept=shooter->y-FixedMul(dist,sintable[angle]); } z=-32; // bullethole=5; } else { int dx,dy,xydist; #define CORNERVALUE 0x500 if (IsWindow(grid[0],grid[1])) return; if (lastcnt<0) { xintercept=grid[0]<<16; if (smokecondition==gs_door) { if (thedir[0]<0) xintercept+=0x9fff; else xintercept+=0x5fff; yintercept=FixedScale(xintercept-shooter->x,vy,vx)+shooter->y; if ((yintercept>>16)!=grid[1]) { if ((yintercept>>16)>grid[1]) yintercept=(grid[1]<<16)+0xffff; else yintercept=(grid[1]<<16); } } else if (smokecondition==gs_wall) { if (thedir[0]<0) { objtype * ta; xintercept += 0x10000; yintercept=FixedScale(xintercept-shooter->x,vy,vx)+shooter->y; xintercept += SMOKEWALLOFFSET; bullethole=1; if (yintercept < ((grid[1] << 16) + CORNERVALUE)) bullethole = 0; else if (yintercept > ((grid[1] << 16) + 0x10000 - CORNERVALUE)) bullethole = 0; ta = (objtype*)actorat[grid[0]][grid[1]]; if ((ta) && (ta->which==PWALL)) bullethole=0; } else { objtype * ta; yintercept=FixedScale(xintercept-shooter->x,vy,vx)+shooter->y; xintercept-=SMOKEWALLOFFSET; bullethole=2; if (yintercept < ((grid[1] << 16) + CORNERVALUE)) bullethole = 0; else if (yintercept > ((grid[1] << 16) + 0x10000 - CORNERVALUE)) bullethole = 0; ta = (objtype*)actorat[grid[0]][grid[1]]; if ((ta) && (ta->which==PWALL)) bullethole=0; } } } else { yintercept=grid[1]<<16; if (smokecondition==gs_door) { if (thedir[1]<0) yintercept+=0x9fff; else yintercept+=0x5fff; xintercept=FixedScale(yintercept-shooter->y,vx,vy)+shooter->x; if ((xintercept>>16)!=grid[0]) { if ((xintercept>>16)>grid[0]) xintercept=(grid[0]<<16)+0xffff; else xintercept=(grid[0]<<16); } } else if (smokecondition==gs_wall) { if (thedir[1]<0) { objtype * ta; yintercept += 0x10000; xintercept=FixedScale(yintercept-shooter->y,vx,vy)+shooter->x; yintercept += SMOKEWALLOFFSET; bullethole=3; if (xintercept < ((grid[0] << 16) + CORNERVALUE)) bullethole = 0; else if (xintercept > ((grid[0] << 16) + 0x10000 - CORNERVALUE)) bullethole = 0; ta = (objtype*)actorat[grid[0]][grid[1]]; if ((ta) && (ta->which==PWALL)) bullethole=0; } else { objtype * ta; xintercept=FixedScale(yintercept-shooter->y,vx,vy)+shooter->x; yintercept-=SMOKEWALLOFFSET; bullethole=4; if (xintercept < ((grid[0] << 16) + CORNERVALUE)) bullethole = 0; else if (xintercept > ((grid[0] << 16) + 0x10000 - CORNERVALUE)) bullethole = 0; ta = (objtype*)actorat[grid[0]][grid[1]]; if ((ta) && (ta->which==PWALL)) bullethole=0; } } } dx = xintercept - shooter->x; dy = shooter->y - yintercept; xydist = FindDistance(dx,dy); if (shooter->obclass==playerobj) { playertype * pstate; M_LINKSTATE(shooter,pstate); z=shooter->z-FixedMulShift(xydist,tantable[yzangle],25)+pstate->playerheight-32; } else z=shooter->z-FixedMulShift(xydist,tantable[yzangle],25); if (smokecondition==gs_wall) { if (z<-32) z=-32; } } SpawnGunSmoke(xintercept,yintercept,z,angle,bullethole); } /* ===================== = = T_BossDied () = ===================== */ void T_BossDied (objtype *ob) { if (ob->ticcount) return; switch (ob->obclass) { case b_darianobj: case b_heinrichobj: case b_darkmonkobj: case b_robobossobj: case b_darksnakeobj: playstate = ex_bossdied; break; default: ; } } /* ===================== = = T_Wind () = ===================== */ static int WindDistance = 1000; static int WindCurrentDistance = 1000; static int WindHandle = -1; static int WindLastTic = -1; static int WindPlaying = false; static int WindPitch = 0; static int WindDestPitch = 0; static int WindPitchRate = 0; void T_Wind ( objtype *ob ) { int distance; int dx; int dy; if ( ( GetTicCount() - WindLastTic ) > 0 ) { WindDistance = 1000; WindPitch += WindPitchRate; if ( WindPitch == WindDestPitch ) { WindDestPitch = ( RandomNumber( "Wind Pitch", 0 ) - 128 ) << 3; WindPitchRate = 1; if ( WindDestPitch < WindPitch ) { WindPitchRate = -WindPitchRate; } } } WindLastTic = GetTicCount(); dx = ( ob->x - PLAYER[0]->x ); dy = ( PLAYER[0]->y - ob->y ); distance = 1000; if ( areabyplayer[ ob->areanumber ] ) { distance = ( FindDistance( dx, dy ) ) >> 13; } if ( distance < WindDistance ) { WindDistance = distance; } if ( WindDistance < 255 ) { WindPlaying = true; WindCurrentDistance = WindDistance; } else { if ( WindPlaying ) { WindCurrentDistance += 3; } } if ( WindPlaying ) { if ( WindCurrentDistance < 255 ) { if ( !SD_SoundActive( WindHandle ) ) { WindHandle = SD_PlayPitchedSound( SD_WINDSND, 255 - WindCurrentDistance, WindPitch ); } else { SD_SetSoundPitch( WindHandle, WindPitch ); SD_SetPan( WindHandle, 255 - WindCurrentDistance, 255 - WindCurrentDistance, 255 - WindCurrentDistance ); } } else { SD_StopSound( WindHandle ); WindPlaying = false; } } } /* ===================== = = StopWind () = ===================== */ void StopWind ( void ) { objtype *temp; FX_SetReverb( 0 ); SD_StopSound( WindHandle ); WindDistance = 1000; WindCurrentDistance = 1000; WindHandle = -1; WindLastTic = -1; WindPlaying = false; WindPitch = 0; WindDestPitch = 0; WindPitchRate = 0; for(temp=FIRSTACTOR; temp; temp=temp->next) { if (temp->soundhandle != -1) SD_StopSound(temp->soundhandle); } }