// This file is distributed under a BSD license. See LICENSE.txt for details. #include "kkriegergame.hpp" #include "genmesh.hpp" #include "engine.hpp" #include "genoverlay.hpp" #if sPROFILE #include "_util.hpp" sMAKEZONE(GameLogic ,"GameLogic" ,0xff224466); sMAKEZONE(GameCollide ,"GameCollide",0xff336699); sMAKEZONE(GamePhysic ,"GamePhysic" ,0xff55aaff); sMAKEZONE(GameAI ,"GameAI" ,0xff77eeff); #endif //#define OVERLAP (0.005f) //#define EPSILON (0.0001f) #define EPSILON (4.010e-3f) // determined from level size #define OVERLAP (EPSILON*2) #define MOVE_EPSILON (1e-6f) #define DOUBLECHECK 0 #if !sPROFILE #define GAMEPERF(x) ; #else #define GAMEPERF(x) x++; sInt KKriegerGame::CallFindFirstIntersect; sInt KKriegerGame::CallFindSubIntersect; sInt KKriegerGame::CallMoveParticle; sInt KKriegerGame::CallOutsideMask; sInt KKriegerGame::CallIntersectOut; sInt KKriegerGame::CallIntersectIn; #endif #pragma lekktor(off) //static sInt SomethingBadHasHappened; // collision panic /****************************************************************************/ KKriegerMesh::KKriegerMesh(GenMesh *mesh) { CellCount = mesh->Coll.Count; Cell = new KKriegerMeshCell[CellCount]; for(sInt i=0;iColl[i]; KKriegerMeshCell *outCell = &Cell[i]; for(sInt j=0;j<8;j++) outCell->Vert[j] = mesh->VertPos(inCell->Vert[j]); outCell->Mode = inCell->Mode; outCell->Logic = inCell->Logic; } } KKriegerMesh::~KKriegerMesh() { delete[] Cell; } void KKriegerMesh::Copy(KObject *) { // KKriegerMeshes should *never* get copied around sVERIFYFALSE; } /****************************************************************************/ #define LOGGING 0 /****************************************************************************/ /****************************************************************************/ void KKriegerMonster::Hit(sInt hits,KKriegerGame *game) { // static sInt HitSounds[] = { 53,51,45,39 }; if(Life>0) { if(State==KMS_INRANGE) State = KMS_ACTIVE; if(hits>Armor) Life -= (hits-Armor)+(Armor/4); else Life -= hits/4; // sDPrintF("monster hit for %d, -> %d\n",hits,Life); if(Life<=0) { Life = 0; if(KillSwitch>0) game->Switches[KillSwitch]++; } //sSystem->SamplePlay(HitSounds[GetType()],0.6f); } } void KKriegerMonster::Sound(sInt handle,sF32 volume) { sVector zero; sInt buf; zero.Init(0,0,0,0); #if !sPLAYER if(GenOverlayManager->SoundEnable) #endif { buf = sSystem->SamplePlay(handle,volume); sSystem->Sample3DParam(handle,buf,Collider.Pos,zero,0.2f,160.0f); } } /****************************************************************************/ void KKriegerPlayer::Init() { Life = 100; Armor = 0; Alpha = 0; Beta = 0; Ammo[0] = 100; Ammo[1] = 50; Ammo[2] = 0; Ammo[3] = 0; Weapon[0] = 1; Weapon[1] = 1; Weapon[2] = 1; Weapon[3] = 0; Weapon[4] = 0; Weapon[5] = 0; Weapon[6] = 0; Weapon[7] = 0; LifeMax = 100; ArmorMax = 100; AlphaMax = 100; BetaMax = 8; AmmoMax[0] = 500; AmmoMax[1] = 200; AmmoMax[2] = 100; AmmoMax[3] = 1000; WeaponMax[0] = 1; WeaponMax[1] = 1; WeaponMax[2] = 1; WeaponMax[3] = 1; WeaponMax[4] = 1; WeaponMax[5] = 1; WeaponMax[6] = 1; WeaponMax[7] = 1; CurrentWeapon = 1; NextWeapon = 0; FireKey = 0; UseKey = 0; CoolTimer = 0; } void KKriegerPlayer::Hit(sInt hits) { if(hits>Armor) Life -= (hits-Armor)+(Armor/4); else Life -= hits/4; if(Life<0) Life = 0; if(hits>4) Sound(10); } void KKriegerPlayer::Sound(sInt handle,sF32 volume) { sVector zero; sInt buf; zero.Init(0,0,0,0); #if !sPLAYER if(GenOverlayManager->SoundEnable) #endif { buf = sSystem->SamplePlay(handle,volume); sSystem->Sample3DParam(handle,buf,Collider.Pos,zero,0.2f,160.0f); } } /****************************************************************************/ /****************************************************************************/ void KKriegerGame::Init() { sInputData id; #if sPROFILE sREGZONE(GameLogic); sREGZONE(GameCollide); sREGZONE(GamePhysic); sREGZONE(GameAI); #endif CellAdd.Init(128); CellSub.Init(4096); CellZone.Init(1024); Monsters.Init(64); Shots.Init(64); PlayerPos.Init(); PlayerDir = 0; PlayerLook = 0; PlayerMat.Init(); PlayerCell = 0; Player.Init(); LastTime = sSystem->GetTime(); sSystem->GetInput(0,id); LastMouseX = id.Analog[0]; LastMouseY = id.Analog[1]; LastCamY = 0; CamPos.Init(); Environment = 0; TickCount = 0; PlayerAdds = 0; #if !sPLAYER TickPerFrame = 0; MaxPlanes = 0; #endif sSetMem(Respawn,0,sizeof(Respawn)); #if !sPLAYER FlatMat = new sSimpleMaterial(sINVALID,sMBF_ZOFF|sMBF_BLENDADD|sMBF_DOUBLESIDED,0,0); QuadGeo = sSystem->GeoAdd(sFVF_COMPACT,sGEO_QUAD); LineGeo = sSystem->GeoAdd(sFVF_COMPACT,sGEO_LINE); #endif WeaponEvent.Init(); Flush(); Panic(); sSetMem(Switches,0,KKRIEGER_SWITCHES); Switches[KGS_ONE] = 1; Switches[KGS_GLARE] = 2; Switches[KGS_RESOLUTION] = 1; Switches[KGS_TEXTURES] = 1; Switches[KGS_SHADOWS] = 1; Switches[KGS_MOUSESPEED] = 5; Switches[KGS_BRIGHTNESS] = 5; Switches[KGS_SPECULAR] = 1; WasInOptions = sFALSE; OnOptionsChanged(); } void KKriegerGame::Exit() { #if !sPLAYER if(QuadGeo!=sINVALID) sSystem->GeoRem(QuadGeo); if(LineGeo!=sINVALID) sSystem->GeoRem(LineGeo); QuadGeo = sINVALID; LineGeo = sINVALID; sRelease(FlatMat); #endif Flush(); CellAdd.Exit(); CellSub.Exit(); CellZone.Exit(); Monsters.Exit(); Shots.Exit(); WeaponEvent.Exit(); } void KKriegerGame::Flush() { sInt i,j; for(i=0;iAdds.Exit(); CellAdd[i]->Subs.Exit(); CellAdd[i]->Zones.Exit(); CellAdd[i]->DeleteFaces(); delete CellAdd[i]; } for(i=0;iEvent[j]) { CellZone[i]->Event[j]->Exit(); delete CellZone[i]->Event[j]; CellZone[i]->Event[j] = 0; } } delete CellZone[i]; } for(i=0;iKind = 0x08; sSystem->GetInput(0,id); LastMouseX = id.Analog[0]; LastMouseY = id.Analog[1]; // gameplay init #if LOGGING sDPrintF("KKriegerGame::Panic() called\n"); #endif } /****************************************************************************/ /****************************************************************************/ void KKriegerGame::SetPainter(KOp *op,KEnvironment *kenv) { while(op && op->Cache) { if(op->Cache->ClassId == KC_IPP || op->Cache->ClassId == KC_DEMO) op = op->GetInput(0); else if(op->CheckOutput(KC_SCENE)) { SetScene((GenScene *)op->Cache); break; } else break; } } void KKriegerGame::SetScene(GenScene *scene) { sMatrix mat; Flush(); mat.Init(); CellList.Init(); SetSceneR(scene,mat,scene,0); #if sPLAYER SetSceneR(scene,mat,scene,1); #endif CellConnect(); #if !sPLAYER sInt i,j; KKriegerMonster *mon; if(CellAdd.Count) { sVector globmin,globmax; sF32 maxworld; sInt i; // find global min/max coord globmin = CellAdd[0]->BBMin; globmax = CellAdd[0]->BBMax; for(i=0;iBBMin.x); globmin.y = sMin(globmin.y,CellAdd[i]->BBMin.y); globmin.z = sMin(globmin.z,CellAdd[i]->BBMin.z); globmax.x = sMax(globmax.x,CellAdd[i]->BBMax.x); globmax.y = sMax(globmax.y,CellAdd[i]->BBMax.y); globmax.z = sMax(globmax.z,CellAdd[i]->BBMax.z); } // find max world coordinate maxworld = 0.0f; maxworld = sMax(maxworld,sFAbs(globmin.x)); maxworld = sMax(maxworld,sFAbs(globmin.y)); maxworld = sMax(maxworld,sFAbs(globmin.z)); maxworld = sMax(maxworld,sFAbs(globmax.x)); maxworld = sMax(maxworld,sFAbs(globmax.y)); maxworld = sMax(maxworld,sFAbs(globmax.z)); // print world scale static sF32 dynRange = 1.0f / 1048576.0f; // 2**(-20) sDPrintF("max world coordinate is %.2f\n",maxworld); if(maxworld * dynRange >= EPSILON) { sDPrintF("warning, kkriegergame epsilon too small!\n"); sDPrintF("use at least %e to guarantee ok precision.\n",maxworld * dynRange); } } // this fixes // FlushCells for(i=0;iCollider.Cell = FindCell(mon->Collider.Pos); for(j=0;j<8;j++) mon->WalkMem.FootCell[j] = mon->Collider.Cell; } #endif CellList.Exit(); } void KKriegerGame::SetMesh(GenMesh *mesh) { sMatrix mat; KKriegerMesh *tempMesh; Flush(); mat.Init(); CellList.Init(); tempMesh = new KKriegerMesh(mesh); AddMesh(tempMesh,mat,0); tempMesh->Release(); CellConnect(); CellList.Exit(); } void KKriegerGame::SetPlayer(const sVector &p,sF32 d,sF32 l) { sInputData id; sMatrix mat; sVector v; sSystem->GetInput(0,id); LastMouseX = id.Analog[0]; LastMouseY = id.Analog[1]; PlayerPos = p; PlayerDir = d; PlayerLook = l; PlayerCell = FindCell(PlayerPos); if(PlayerCell==0 && CellAdd.Count>0) { PlayerCell = CellAdd[0]; PlayerPos.Add3(CellAdd[0]->BBMin,CellAdd[0]->BBMax); PlayerPos.Scale3(0.5f); } LastCamY = p.y; CamPos = p; Player.Collider.Pos = PlayerPos; Player.Collider.Cell = PlayerCell; Player.Collider.Radius = 0.5f; // Player.Collider.Init(PlayerPos, PlayerCell); Player.Collider.OldPos = Player.Collider.Pos; mat.Init(); mat.l.Init(0,-0.25f,0,1.0f); v.Init(0.75f,2.5f,0.75f,0); Player.HitCell.Init(mat,v,KCM_ZONE); mat.l = PlayerPos; mat.l.y += 0.85f; Player.HitCell.PrepareDynamic(mat); // Panic(); } /****************************************************************************/ void KKriegerGame::SetSceneR(GenScene *scene,const sMatrix &base,GenScene *usescene,sInt phase) { sMatrix mat,srt,tmat; sInt i,j,max; srt.InitSRT(scene->SRT); max = scene->Count; if(max==0) max = 1; if(scene->IsSector) usescene = scene; mat = base; for(i=0;iCount==0 || i!=0) { tmat.MulA(srt,mat); mat = tmat; } for(j=0;jChilds.Count;j++) SetSceneR(scene->Childs.Get(j),mat,usescene,phase); #if sLINK_KKRIEGER if(scene->CollMesh) { if(phase == 0) AddMesh(scene->CollMesh,mat,usescene); else { #if sPLAYER // scene->CollMesh->Release(); // scene->CollMesh = 0; #endif } } #endif } } void KKriegerGame::AddMesh(KKriegerMesh *mesh,const sMatrix &mat,GenScene *usescene) { sInt i,j,k; KKriegerCell *kc; KKriegerCellAdd *kca; KKriegerMeshCell *mc; sVector va[8]; sInt mode; for(j=0;jCellCount;j++) { mc = &mesh->Cell[j]; mode = mc->Mode & 3; for(k=0;k<8;k++) va[k].Rotate34(mat,mc->Vert[k]); switch(mode) { default: #if !sINTRO sVERIFYFALSE; #endif case KCM_ADD: kca = new KKriegerCellAdd; *CellAdd.Add() = kca; kca->Adds.Init(); kca->Subs.Init(); kca->Zones.Init(); kca->Faces = 0; kc = kca; break; case KCM_SUB: kc = new KKriegerCell; *CellSub.Add() = kc; break; case KCM_ZONE: kc = new KKriegerCell; *CellZone.Add() = kc; break; } kc->Init(va,mc->Mode); kc->Logic = mc->Logic; kc->Scene = usescene; for(i=0;i<2;i++) { kc->Event[i] = 0; if(kc->Logic.Event[i]) { kc->Event[i] = new KEvent; kc->Event[i]->Init(); kc->Event[i]->Op = kc->Logic.Event[i]; kc->Event[i]->Translate.x = (kc->BBMin.x + kc->BBMax.x)/2; kc->Event[i]->Translate.y = kc->BBMin.y; kc->Event[i]->Translate.z = (kc->BBMin.z + kc->BBMax.z)/2; kc->Event[i]->CullDist = 35; } } #if !sPLAYER MaxPlanes = sMax(MaxPlanes,kc->PlaneCount); #endif *CellList.Add() = kc; } } /****************************************************************************/ // For heapsort in CellConnect. void KKriegerGame::CellSiftDown(sInt n,sInt k) { KKriegerCell *v = CellList[k]; sF32 key = v->BBMin.x; while(k < (n >> 1)) { sInt j = k*2+1; if(j+1 < n && CellList[j]->BBMin.x < CellList[j+1]->BBMin.x) j++; if(key >= CellList[j]->BBMin.x) break; CellList[k] = CellList[j]; k = j; } CellList[k] = v; } //sInt debug_count_splits; // Connect cells. Uses CellHeapSort. void KKriegerGame::CellConnect() { // Heapsort the list of cells by minimum x coordinate of bbox. sInt n = CellList.Count; for(sInt k=n/2-1;k>=0;k--) CellSiftDown(n,k); while(--n > 0) { sSwap(CellList[0],CellList[n]); CellSiftDown(n,0); } // Verify the resulting list is sorted for(sInt i=0;iBBMin.x <= CellList[i+1]->BBMin.x); // Sweep-and-prune the list, connecting cells as we identify pairs. n = CellList.Count; for(sInt box1=0;box1 < n;box1++) { KKriegerCell *cell1 = CellList[box1]; sInt cell1m = cell1->Mode & 3; sF32 maxKey = cell1->BBMax.x; for(sInt box2=box1+1;box2 < n && CellList[box2]->BBMin.x <= maxKey;box2++) { KKriegerCell *cell2 = CellList[box2]; sInt cell2m = cell2->Mode & 3; if(cell1m == KCM_ADD || cell2m == KCM_ADD) { // BBoxes overlap in x direction, and at least one of them is an add. // Check whether bboxes really intersect. if(cell1->BBMax.z < cell2->BBMin.z || cell1->BBMin.z > cell2->BBMax.z) continue; if(cell1->BBMax.y < cell2->BBMin.y || cell1->BBMin.y > cell2->BBMax.y) continue; // We have actual overlap (of bboxes atleast). Now connect. if(cell1m == KCM_ADD && cell2m == KCM_ADD) { // Adds need to link to each other, so this is a special case. KKriegerCellAdd *c1a = (KKriegerCellAdd *) cell1; KKriegerCellAdd *c2a = (KKriegerCellAdd *) cell2; *c1a->Adds.Add() = c2a; *c2a->Adds.Add() = c1a; } else // only is one add { KKriegerCellAdd *cella; // add cell KKriegerCell *cello; // other cell sInt type; if(cell1m == KCM_ADD) // first is add { cella = (KKriegerCellAdd *) cell1; cello = cell2; type = cell2m; } else // second is add { cella = (KKriegerCellAdd *) cell2; cello = cell1; type = cell1m; } if(type == KCM_SUB) *cella->Subs.Add() = cello; else if(type == KCM_ZONE) *cella->Zones.Add() = cello; } } } } // now generate the collision geometrie of the adds // sInt time = sSystem->GetTime(); for(sInt i = 0; i < CellAdd.Count; i++) { // debug_count_splits = 0; CreateCollisionFaces(*CellAdd[i]); // sDPrintF("(%2d)%5d ",CellAdd[i]->Adds.Count,debug_count_splits); // if(!(i%15)) sDPrintF("\n"); } // sDPrintF("Create it all %d\n",sSystem->GetTime()-time); } /****************************************************************************/ /****************************************************************************/ #pragma lekktor(off) sBool KKriegerGame::OnKey(sU32 key) { sInt i; static sInt CheatOn; static sS8 weaponswap[8] = {-1,0,1,2,4,6,-1,-1}; LastKey = key&0x8001ffff; switch(key&(0x8001ffff)) { case ' ': if(OnGround && JumpTimer==0) { SpeedUp = JumpForce; PlayerSound(4,0.9f); JumpTimer = 10; } break; case 'k': case 'K': Player.Hit(10); break; #if !sPLAYER case 'i': case 'I': Switches[KGS_GAME] = KGS_GAME_INTRO; break; #endif case 'r': case 'R': #if !sPLAYER Restart(); #endif break; case 'w': case 'W': AccelForw = AccelForwFactor; CheatOn = 0; return sTRUE; case 's': case 'S': AccelForw = -AccelBackFactor; CheatOn = 0; return sTRUE; case 'd': case 'D': AccelSide = AccelSideFactor; CheatOn = 0; return sTRUE; case 'a': case 'A': AccelSide = -AccelSideFactor; CheatOn = 0; return sTRUE; case 'w'|sKEYQ_BREAK: case 'W'|sKEYQ_BREAK: case 's'|sKEYQ_BREAK: case 'S'|sKEYQ_BREAK: AccelForw = 0; CheatOn = 0; return sTRUE; case 'd'|sKEYQ_BREAK: case 'D'|sKEYQ_BREAK: case 'a'|sKEYQ_BREAK: case 'A'|sKEYQ_BREAK: AccelSide = 0; CheatOn = 0; return sTRUE; case 'y': case 'Y': FlyMode = !FlyMode; return sTRUE; case sKEY_SHIFTL: Player.UseKey = 1; return sTRUE; case sKEY_CTRLR: case sKEY_MOUSEL: Player.FireKey = 1; return sTRUE; case sKEY_CTRLR|sKEYQ_BREAK: case sKEY_MOUSEL|sKEYQ_BREAK: Player.FireKey = 0; return sTRUE; #if !sPLAYER case 'z': CamZoomPos = (CamZoomPos+1)%2; return sTRUE; #endif case 'm': case 'M': CheatOn = 1; return sTRUE; case 'n': case 'N': if(CheatOn) sCopyMem(&Player.Life,&Player.LifeMax,16*4); CheatOn = 0; return sTRUE; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if(CheatOn) { RespawnAt(key&15); CheatOn = 0; } else { i = weaponswap[key&7]; if(i>=0 && Player.Weapon[i]) Player.NextWeapon = i; } return sTRUE; } return sFALSE; } /****************************************************************************/ void KKriegerGame::AddEvents(KEnvironment *kenv) { sInt i; KKriegerShot *shot; KKriegerCell *cell; for(i=0;iEvent->Select) { Shots[i] = Shots[--Shots.Count]; } else { i++; if(shot->Mode!=0) { shot->Event->Matrix = shot->Matrix; kenv->AddStaticEvent(shot->Event); } } } for(i=0;iEvent[0] && cell->Respawn==0) kenv->AddStaticEvent(cell->Event[0]); if(cell->Event[1] && cell->Respawn>0) kenv->AddStaticEvent(cell->Event[1]); } if(Player.NextWeapon!=Player.CurrentWeapon && WeaponTimer>=1.0f) { Player.CurrentWeapon = Player.NextWeapon; WeaponEvent.Exit(); WeaponEvent.Init(); WeaponEvent.Op = WeaponOptics[Player.CurrentWeapon]; WeaponTimer = 0.0f; } if(WeaponEvent.Op) { WeaponEvent.Translate = PlayerPos; WeaponEvent.Translate.y = LastCamY+CamOffset, WeaponEvent.Rotate.Init(PlayerLook/sPI2F,PlayerDir/sPI2F,0,0); WeaponEvent.Velocity = WeaponTimer; WeaponEvent.Modulation = 0; if(WeaponCool[Player.CurrentWeapon]>0.01f) WeaponEvent.Modulation = (WeaponCool[Player.CurrentWeapon]-Player.CoolTimer)/WeaponCool[Player.CurrentWeapon]; kenv->AddStaticEvent(&WeaponEvent); } } /****************************************************************************/ void KKriegerGame::ResetRoot(KEnvironment *env,KOp *root,sBool firsttime) { SetPainter(root,env); Restart(); if(firsttime) Switches[KGS_GAME] = KGS_GAME_INTRO; } sInt KKriegerGame::GetNewRoot() { #if sLINK_KKRIEGER sInt sw = Switches[KGS_GAME]; sInt mode = 1; if(sw==KGS_GAME_RUN || sw==KGS_GAME_INGAME || sw==KGS_GAME_RESTART) mode = 2; if(sw==KGS_GAME_INTRO) mode = 0; if(sw==KGS_GAME_QUIT) { sSystem->Exit(); Switches[KGS_GAME] = KGS_GAME_START; } if(sw==KGS_GAME_RESTART) { Switches[KGS_GAME] = KGS_GAME_RUN; Restart(); } return mode; #else return 0; #endif } void KKriegerGame::Restart() { sInt i; ResetByOp = 1; SetPlayer(PlayerStartPos,0,0); // RespawnAt(1); LastCamY = PlayerPos.y; CamPos = PlayerPos; for(i=0;iState = KMS_RESPAWN; for(i=0;iDoRespawn(); sSetMem(Switches+16,0,KKRIEGER_SWITCHES-16); // Panic(); } void KKriegerGame::Continue() { sInt i; SetPlayer(PlayerStartPos,0,0); for(i=0;i<16;i++) { if(Switches[KGS_RESPAWN+i] && Respawn[i].Valid) SetPlayer(Respawn[i].Pos,Respawn[i].Dir,0); } for(i=0;iLife>0) Monsters[i]->State = KMS_RESPAWN; Player.Life = 100; if(Player.Ammo[0]<200) Player.Ammo[0] = 200; } sInt KKriegerGame::SplashCalc(const sVector &pos,KKriegerCellAdd *cell) { sVector v,p1; sF32 dist; KKriegerCollideInfo ci; v.Sub3(pos,SplashPos); dist = v.Abs3(); if(dist=0 && weapon<8); info = &ShotInfoTable[weapon]; if(info->Mode==0) return; shot = Shots.Add(); sSetMem(shot,0,sizeof(*shot)); shot->Mode = info->Mode; shot->Tensor.Init(0,0,0,0); shot->Weapon = weapon; shot->Event = new KEvent; shot->Event->Init(); shot->Event->Active = 0; shot->Matrix.Init(); shot->DoubleKill = 0; if(monster) { // mat = monster->Matrix; mat.InitDir(*monsterfiredir); mat.l = monster->Collider.Pos; mat.l.y += 0.75f; // v.Init(info->px,info->py,info->pz); // v.Rotate3(mat); // mat.l.Add3(v); skipcell = 0; shot->Who = KCRF_ISMONSTER; shot->Cell = monster->Collider.Cell; } else { mat.InitEuler(PlayerLook+info->rx*sPI2F,PlayerDir,0); mat.l.x = info->px; mat.l.y = info->py; mat.l.z = info->pz; mat.l.Rotate3(mat); mat.l.x += PlayerPos.x; mat.l.y += LastCamY+CamOffset; mat.l.z += PlayerPos.z; skipcell = &kenv->Game->Player.HitCell; shot->Who = KCRF_ISPLAYER; shot->Cell = kenv->Game->PlayerCell; } speed = mat.k; speed.Scale4(info->Speed); scale.Init(0.5f,0.5f,0.5f); shot->Event->Matrix = mat; shot->Matrix.l = mat.l; shot->OldPos.Sub4(shot->Matrix.l,speed); if(info->Mode==2) { shot->Tensor.InitRnd(); shot->Tensor.Scale3(0.01f); shot->Tensor.w = 0; } shot->Event->Op = WeaponShot[weapon]; shot->Event->Monster = monster; } extern "C" int __cdecl wsprintfA(sChar *buffer,const sChar *format,...); extern "C" void __stdcall OutputDebugStringA(const sChar *str); void KKriegerGame::OnTick(KEnvironment *kenv,sInt slices) { sInputData id; sInt time; sF32 d; sVector speed; sInt i; KKriegerMonster *mon; sInt sl; sBool inOptions; sF32 f; KKriegerCollideInfo ci; inOptions = Switches[KGS_GAME] == KGS_GAME_OPTIONS || Switches[KGS_GAME] == KGS_GAME_INGAME && Switches[KGS_INGAME_MENU] == 1; if(WasInOptions && !inOptions) OnOptionsChanged(); WasInOptions = inOptions; // check if everything is safe. if(PlayerCell==0 || Switches[KGS_GAME]!=KGS_GAME_RUN) return; TickCount += slices; // sDPrintF("slices %d\n",slices); // get mouse looking Environment = kenv; #if DOUBLECHECK slices = 1; #endif #if !sPLAYER TickPerFrame = slices; #endif time = sSystem->GetTime()-LastTime; // don't get mouse movement if a lot of time ticked away LastTime += time; sSystem->GetInput(0,id); if(time<1000) { f = MouseTurnSpeed*(Switches[KGS_MOUSESPEED]+2)/7; PlayerDir += (id.Analog[0] - LastMouseX)*f; f = MouseLookSpeed*(Switches[KGS_MOUSESPEED]+2)/7*(-Switches[KGS_MOUSEINVERT]*2+1); PlayerLook += (id.Analog[1] - LastMouseY)*f; PlayerLook = sRange(PlayerLook,sPIF/2,-sPIF/2); } LastMouseX = id.Analog[0]; LastMouseY = id.Analog[1]; PlayerMat.InitEuler(FlyMode?PlayerLook:0,PlayerDir,0); // weapon animation if(Player.NextWeapon!=Player.CurrentWeapon) { if(WeaponTimer<0.75f) WeaponTimer += slices*0.01f*5; WeaponTimer += slices*0.01f; } else { if(WeaponTimer<0.25f) { WeaponTimer+=slices*0.01f; if(WeaponTimer>0.25f) WeaponTimer = 0.25f; } else { if(WeaponTimer>0.251f || AccelForw>0) { WeaponTimer+=slices*0.003f; if(WeaponTimer>0.75f) { if(AccelForw>0) { WeaponTimer-=0.5f; if(WeaponTimer>0.75) WeaponTimer = 0.25f; } else WeaponTimer = 0.25f; } } } } // player step if(AccelForw || AccelSide) { StepTimer+=slices*0.01f; if(StepTimer>0.5f && OnGround) // issue player step { StepTimer -= 0.5f; StepFoot = 1-StepFoot; PlayerSound(StepFoot,0.8f); } } // splash damage if(SplashEnable) { KKriegerCellAdd *cell; cell = FindCell(SplashPos); for(i=0;iGame->Monsters.Get(i); mon->Hit(SplashCalc(mon->Collider.Pos,cell),this); } SplashHits = SplashHits/2; Player.Hit(SplashCalc(PlayerPos,cell)); SplashEnable = 0; } // fire? if(Player.FireKey) { if(!Player.Ammo[Player.CurrentWeapon/2] && Player.CurrentWeapon!=0) PlayerSound(15,0.8f); if((Player.Ammo[Player.CurrentWeapon/2]>0 || Player.CurrentWeapon==0) && Player.CurrentWeapon==Player.NextWeapon && Player.CoolTimer<=0 && WeaponTimer>=0.25f) { if(Player.CurrentWeapon!=0) Player.Ammo[Player.CurrentWeapon/2]--; Player.CoolTimer = WeaponCool[Player.CurrentWeapon]; if(!(WeaponFlags[Player.CurrentWeapon]&KWF_CONTINUOUS)) Player.FireKey = 0; if(WeaponShot[Player.CurrentWeapon]) { FireShot(kenv,Player.CurrentWeapon,0,0); } if(!(WeaponFlags[Player.CurrentWeapon]&KWF_CONTINUOUS)) Player.FireKey = 0; } } if(Player.CoolTimer>=0) Player.CoolTimer -= slices*0.01f; // the timesliced-loop ... for(sl=0;sl0) JumpTimer--; sF32 testh = StairHeight + GravityPlayer + Player.Collider.Radius; d = testh; if(Player.Collider.Cell) { sVector p0,p1; KKriegerCellAdd *cell; ci.Init(0,KCRF_ISPLAYER); p0 = Player.Collider.Pos;//part->Pos; p1 = p0; p1.y -= testh; cell = Player.Collider.Cell; // cell might get changed by CollideRay! if(CollideRay(cell,p1,p0,ci)) d += ci.Dist; } // move player speed.Init(); speed.Sub4(Player.Collider.Pos,Player.Collider.OldPos); speed.Scale3(1.0f-(OnGround?DampPlayerGround:DampPlayerAir)); speed.AddScale3(PlayerMat.i,AccelSide); speed.AddScale3(PlayerMat.k,AccelForw); speed.y += SpeedUp; SpeedUp = 0; OnGround = 0; upforce = 0; if(d= 0.4f) // player was flying, issue "land" sample { PlayerSound(5); StepFoot = 0; StepTimer = 0; } OnGround = 1; OffGroundTime = 0.0f; upforce += (testh-d)*0.1f; } else { OffGroundTime += slices*0.01f; speed.y -= GravityPlayer; } // sDPrintF("speed %f pos %f dist %f Ground %d %08x\n",speed.y,Player.Collider.Pos.y,d,OnGround,Player.Collider.Cell); // upforce = 0; speed.y-=GravityPlayer; // slide on the ground Player.Collider.OldPos = Player.Collider.Pos; Player.Collider.OldPos.y += upforce; speed.y += upforce; MoveCollider(Player.Collider,speed,KCRF_ISPLAYER); PlayerPos = Player.Collider.Pos; if(Player.Collider.Cell) PlayerCell = Player.Collider.Cell; Player.HitCell.Matrix1.InitEuler(0,PlayerDir,0); Player.HitCell.Matrix1.l = PlayerPos; Player.HitCell.Matrix1.l.y += 0.85f; Player.HitCell.Matrix0.l = Player.HitCell.Matrix1.l; // slice Physic((sl+1.0f)/slices); Zones(kenv); // cam LastCamY = sFade(LastCamY,PlayerPos.y,DampPlayerCam); CamPos = PlayerPos; CamPos.y = LastCamY; CamPos.y += CamOffset; // 3d sound listener speed.Scale3(1000.0f / time); sSystem->Sample3DListener(CamPos,speed,PlayerMat.j,PlayerMat.k); // AI { sBool prev; sBool mach; prev = 0; mach = 0; #if sPROFILE sZONE(GameAI); #endif MonsterMagnetAI(); for(i=0;iLife>0; if(mon->Flags&KMF_MACHINE) { mach = prev; prev = 0; // HACK: first monster from machine spawns even with KMF_WHENPREVDEAD set. makes editing simpler. } } } for(i=0;iOutsideMask(PlayerPos)!=0) { KKriegerCellAdd *cell; cell = FindCell(PlayerPos); if(cell!=0) { PlayerCell = cell; #if LOGGING sDPrintF("KKriegerGame::OnTick() corrected player pos\n"); #endif } } } // finally move collisions for(i=0;iBlocked) DCellPtr[i]->Matrix0 = DCellPtr[i]->Matrix1; Environment = 0; // check death of player if(Player.Life==0 && Switches[KGS_GAME]==KGS_GAME_RUN) { //Switches[KGS_GAME] = KGS_GAME_LOST; Continue(); } // diagnostics #if !sPLAYER { sInt i; KKriegerCellAdd *cell,*test; cell = PlayerCell; PlayerAdds = 1; for(i=0;iAdds.Count;i++) { test = cell->Adds.Get(i); if(test->OutsideMask(PlayerPos,0)==0) PlayerAdds++; } } #endif } /****************************************************************************/ // check all collision zones... void KKriegerGame::Zones(KEnvironment *kenv) { sInt *valp; sInt i,action,out; KKriegerCell *cell; KKriegerCellDynamic *dcell; #if sPROFILE sZONE(GameLogic); #endif for(i=0;iMode==KCM_ZONE); // respawn if(cell->Respawn>0 && cell->Respawn<0x40000000) { if(cell->Respawn<=10) cell->DoRespawn(); else cell->Respawn -= 10; } // logic if(Switches[cell->Logic.Condition]) { action = 0; switch(cell->Logic.Code&0xf0) { case KKEE_ENTER: if(cell->Enter&KPM_PLAYER) action = 1; break; case KKEE_LEAVE: if(cell->Leave&KPM_PLAYER) action = 1; break; case KKEE_COLLECT: if((cell->Collided&KPM_PLAYER) && cell->Respawn==0) { action = 1; cell->Respawn = 0x7fffffff; } break; case KKEE_RESPAWN0: if((cell->Collided&KPM_PLAYER) && cell->Respawn==0) { action = 1; cell->Respawn = 10*1000; } break; case KKEE_RESPAWN1: if((cell->Collided&KPM_PLAYER) && cell->Respawn==0) { action = 1; cell->Respawn = 30*1000; } break; case KKEE_RESPAWN2: if((cell->Collided&KPM_PLAYER) && cell->Respawn==0) { action = 1; cell->Respawn = 60*1000; } break; case KKEE_RESPAWN3: if((cell->Collided&KPM_PLAYER) && cell->Respawn==0) { action = 1; cell->Respawn = 5*60000; } break; case KKEE_HIT: if(cell->Enter&KPM_WEAPONS) action = 1; break; case KKEE_USE: if((cell->Collided&KPM_PLAYER) && Player.UseKey) action = 1; break; case KKEE_ALWAYS: action = 1; break; case KKEE_INSIDE: action = (cell->Collided&KPM_PLAYER)?1:0; break; case KKEE_HITORENTER: if(cell->Enter&(KPM_PLAYER|KPM_WEAPONS)) action = 1; break; } valp = (&Player.Life)+(cell->Logic.Output&15); switch(cell->Logic.Code&0x0f) { case KKEC_ENABLESWITCH: if(action) Switches[cell->Logic.Output] = 1; break; case KKEC_DISABLESWITCH: if(action) Switches[cell->Logic.Output] = 0; break; case KKEC_TOGGLESWITCH: if(action) Switches[cell->Logic.Output] = !Switches[cell->Logic.Output]; break; case KKEC_SETSWITCH: Switches[cell->Logic.Output] = action; break; case KKEC_SETVALUE: if(action) *valp = cell->Logic.Value; break; case KKEC_MAXVALUE: if(action) *valp = sMax(*valp,cell->Logic.Value); break; case KKEC_MINVALUE: if(action) *valp = sMin(*valp,cell->Logic.Value); break; case KKEC_INCVALUE: if(action) { out = cell->Logic.Output&15; if(/*(&Player.Life)[out]==0 &&*/ out>=8) // always collect weapon, auch wenn man die waffe schon besitzt Player.NextWeapon = out-8; *valp = sMin(*valp+cell->Logic.Value,(&Player.LifeMax)[out]); } break; case KKEC_DECVALUE: if(action) *valp = sMax(*valp-cell->Logic.Value,0); break; case KKEC_JUMP: if(action) SpeedUp += 0.001f * cell->Logic.Value; break; case KKEC_RESPAWN: if(action) Switches[KGS_RESPAWN+(cell->Logic.Value&15)]=1; break; } } // done cell->Enter = 0; cell->Leave = 0; cell->Collided = 0; } // reset cell enter/leave info for(i=0;iEnter = 0; dcell->Leave = 0; dcell->Collided = 0; } } /****************************************************************************/ void KKriegerGame::RespawnAt(sInt i) { if(Respawn[i].Valid || i==1) { SetPlayer(Respawn[i].Pos,Respawn[i].Dir,0); PlayerSound(11); } } /****************************************************************************/ void KKriegerGame::OnPaint(sMaterialEnv *env) { #if !sINTRO sInt qc,qcmax; sVector v0,v1; sMatrix mat; sU32 col; qcmax = 0x4000; qc = 0; col = 0xff301020; sSystem->GetTransform(sGT_MODELVIEW,mat); mat.Trans3(); v0 = mat.i; v0.Scale3(0.125f); v1 = mat.j; v1.Scale3(0.125f); #endif } /****************************************************************************/ void KKriegerGame::OnOptionsChanged() { // update engine usage mask using "shadows" switch sU32 usageMask = ~0U & ~(1<SetUsageMask(usageMask); // update resolution if necessary static const sInt xRes[] = { 640,800,1024,1280 }; static const sInt yRes[] = { 480,600, 768,1024 }; sInt res = Switches[KGS_RESOLUTION]; #if sPLAYER if(xRes[res] != sSystem->ConfigX) // resolution changed? { sSystem->ConfigX = xRes[res]; sSystem->ConfigY = yRes[res]; sSystem->InitScreens(); } #endif } /****************************************************************************/ /****************************************************************************/ void KKriegerGame::GetCamera(sMaterialEnv &env) { if(CamZoomPos) { env.CameraSpace.InitEuler(0,PlayerDir,0); env.CameraSpace.l = PlayerPos; env.CameraSpace.l.w = 1.0f; env.CameraSpace.l.AddScale3(env.CameraSpace.k,-2.0f); } else { env.CameraSpace.InitEuler(PlayerLook,PlayerDir,0); env.CameraSpace.l = CamPos; env.CameraSpace.l.w = 1.0f; } env.ZoomX = env.ZoomY = 1.0f; env.Orthogonal = sMEO_PERSPECTIVE; } void KKriegerGame::GetPlayer(sMatrix &m) { m.Init(); m.l.z = -5; } /****************************************************************************/ /*** ***/ /*** Sound ***/ /*** ***/ /****************************************************************************/ void KKriegerGame::PlayerSound(sInt sound,sF32 volume) { sInt buf; sVector zero; zero.Init(0,0,0,0); #if !sPLAYER if(GenOverlayManager->SoundEnable) #endif { buf = sSystem->SamplePlay(sound,volume); sSystem->Sample3DParam(sound,buf,PlayerPos,zero,0.2f,1.0f); } } /****************************************************************************/ /*** ***/ /*** Game Logic ***/ /*** ***/ /****************************************************************************/ struct KKMonsterWalkInfo { sU32 Flags; sInt FootCount; sF32 StepTresh,RunLowTresh,RunHighTresh; sF32 scanup,scandown; sInt fgw; sF32 slw,ssw,snw; sInt fgr; sF32 slr,ssr,snr; sF323 LegPos[8]; }; KKMonsterWalkInfo KKMonsterWalkInfoTable[5] = { { // robospider 2,3,0.150f,0.750f,1.250f,2.000f,2.000f, 3,0.870f,0.390f,0.500f, 3,1.250f,0.290f,0.350f, { { -0.500f,0.125f,0.000f }, { 0.500f,0.125f,0.000f }, { 0.000f,0.205f,0.750f }, } }, { // killerbug 2,6,0.150f,0.750f,1.250f,2.000f,2.000f, 6,1.255f,0.320f,0.545f, 6,2.160f,0.155f,0.210f, { { -1.125f,0,0 }, { 1.125f,0,0 }, { -0.625f,0,0 }, { 0.625f,0,0 }, { -1.000f,0,-0.125f }, { 1.000f,0,-0.125f }, } }, { // hellbug 2,4,0.150f,0.750f,1.250f,2.000f,2.000f, 4,1.130f,0.270f,0.510f, 4,2.160f,0.170f,0.260f, { { -0.600f,0.250f, 0.500f }, { 0.600f,0.250f, 0.500f }, { -0.450f,0.030f,-0.860f }, { 0.450f,0.030f,-0.875f }, } }, { // ultimate bot 2,2,0.150f,1.750f,1.250f,2.000f,2.000f, 2,0.505f,0.920f,1.125f, 2,1.470f,0.800f,0.960f, { { -0.800f,0.125f,0 }, { 0.800f,0.125f,0 }, } }, { // no walking (spawn machines) 2,2,0.150f,1.750f,1.250f,2.000f,2.000f, 2,0.505f,0.920f,1.125f, 2,1.470f,0.800f,0.960f, { { -0.800f,0.125f,0 }, { 0.800f,0.125f,0 }, } } }; void KKMonsterWalk(KEnvironment *kenv,KKMonsterWalkMem *mem,KKMonsterWalkInfo *wi,KSpline *stepspline,sMatrix &matrix,KKriegerCellAdd *moncell) { sMatrix mat;//,save; // local vars sMatrix dir; // desired orientation of walker sVector v,sum,foot[8],spline[8],stepdir; sInt i,j; sInt bestleg; sInt time; sF32 dist,f,t; sInt changestate; KKriegerCellAdd *cell; KKriegerCollideInfo ci; sVector p0,p1; // copy of instance-vars sInt State; sInt LastLeg; sInt NextLeg; // parameters sInt FootGroup[2]; // groups of feet. sF32 StepLength[2]; // max. length of a step sInt StepSpeed[2]; // duration of a step in ms. sInt StepNext[2]; // when to start the next step in ms. steps may overlap // init parameters FootGroup[0] = wi->fgw; FootGroup[1] = wi->fgr; StepLength[0] = wi->slw; StepLength[1] = wi->slr; StepSpeed[0] = sFtol(wi->ssw*1000); StepSpeed[1] = sFtol(wi->ssr*1000); StepNext[0] = sMin(sFtol(wi->snw*1000),StepSpeed[0]); StepNext[1] = sMin(sFtol(wi->snr*1000),StepSpeed[1]); // init instance storage if(mem->Reset || kenv->TimeReset) { mem->Reset = 0; sum.Init(); mem->FootCenter.Init(); mem->InitSteps = wi->FootCount*2; for(i=0;iFootCount;i++) { mem->FootDelta[i].Init(wi->LegPos[i].x,wi->LegPos[i].y,wi->LegPos[i].z,1); p0.Rotate4(matrix,mem->FootDelta[i]); p0.w = 1; p1 = p0; p1.y -= 2.0f; ci.Init(); v = p0; cell = moncell; if(cell && kenv->Game->CollideRay(cell,p1,p0,ci)) v = ci.Pos; v.w = 0; mem->FootOld[i] = mem->FootNew[i] = v; mem->StepTime[i] = StepSpeed[0]; mem->FootCell[i] = cell; mem->FootCenter.x += mem->FootDelta[i].x/wi->FootCount; mem->FootCenter.z += mem->FootDelta[i].z/wi->FootCount; } mem->LastTime = kenv->CurrentTime; mem->NewPos = matrix.l; mem->State = 0; mem->LastLeg = 0; for(i=0;iFootCount;i++) mem->FootDelta[i].Sub3(mem->FootCenter); } State = mem->State; LastLeg = mem->LastLeg; NextLeg = mem->NextLeg; // animate sum.Init(); f = 0; for(i=0;iFootCount;i++) { t = sRange(1.0f*mem->StepTime[i]/StepSpeed[State],1.0f,0.0f); if(iEval((t+State)*0.5f,spline[i]); if(mem->FootDelta[i].x<0) spline[i].x=-spline[i].x; if(mem->FootDelta[i].z<0) spline[i].z=-spline[i].z; spline[i].w = spline[i].w-State; } else { spline[i].x = 0; spline[i].y = sFSin(t*sPI)*0.25f; spline[i].z = 0; spline[i].w = t; } foot[i].Lin3(mem->FootOld[i],mem->FootNew[i],spline[i].w); foot[i].w = 1.0f; /* if(Flags&0x02) { foot[i].y += spline[i].y; mem->FootPart[i].Control(foot[i]); foot[i].y -= spline[i].y; } */ sum.Add3(foot[i]); } sum.Scale3(1.0f/wi->FootCount); mat.i.Init(0,0,0,0); mat.j.Init(0,0,0,0); // ryg 040820 mat.k.Init(0,0,0,0); mat.l.Init(sum.x,sum.y,sum.z,1); for(i=0;iFootCount;i++) { v.Sub3(foot[i],sum); mat.i.AddScale3(v,mem->FootDelta[i].x); mat.j.AddScale3(v,mem->FootDelta[i].y); mat.k.AddScale3(v,mem->FootDelta[i].z); } switch((wi->Flags>>2)&3) { case 0: // straight, copy from input mat.i = matrix.i; mat.j = matrix.j; mat.k = matrix.k; break; case 2: // use xy mat.i.Unit3(); mat.j.Init(0,1,0,0); mat.k.Cross4(mat.i,mat.j); mat.k.Unit3(); mat.j.Cross4(mat.k,mat.i); mat.j.Unit3(); break; case 1: // use y mat.k = matrix.k; case 3: // use xz mat.k.Unit3(); mat.j.Cross4(mat.k,mat.i); mat.j.Unit3(); mat.i.Cross4(mat.j,mat.k); mat.i.Unit3(); break; } t = (f+LastLeg)/FootGroup[State]; if(t>=2.0f) t-=2.0f; if(t>=1.0f) t-=1.0f; t = (t+State)*0.5f; mem->LegTimeOut = t; // advance time = kenv->CurrentTime-mem->LastTime; mem->LastTime = kenv->CurrentTime; if(time<0) time = 0; dir = matrix; if(mem->DeviationFlag>0) { dir.l.Add3(mat.l,mem->Deviation); } dir.k.y = 0; dir.k.Unit3(); dir.j.Init(0,1,0,0); dir.i.Cross4(dir.j,dir.k); for(i=0;iFootCount;i++) { if(mem->StepTime[i]StepTime[i] += time; } // find distance dist = 0; stepdir.Init(); NextLeg = (LastLeg+1)%FootGroup[State]; bestleg = NextLeg; for(i=0;iFootCount;i++) { v.Rotate34(dir,mem->FootDelta[i]); v.Sub3(foot[i]); v.y = 0; f = v.Abs3(); if(f>dist && mem->StepTime[i]>=StepSpeed[State]) { dist = f; stepdir = v; bestleg = i; } v.Rotate3(dir,spline[i]); // kenv->Var[KV_LEG_L0+i].Add3(foot[i],v); // kenv->Var[KV_LEG_L0+i].w = spline[i].w; mem->LegPosOut[i].Add3(foot[i],v); mem->LegPosOut[i].w = spline[i].w; } // change walking/running changestate = 1; for(i=0;iFootCount;i++) if(mem->StepTime[i]wi->RunHighTresh) || (State==1 && distRunLowTresh)) changestate +=2; // changestate = 0 -> in animation -> // changestate = 1 -> animation done. -> issue bestleg when idle // changestate = 2 -> in animation, change state -> don't issue new steps // changestate = 3 -> animation done, change state -> change state really // sDPrintF("%08x:state %d change %d dist %f | %4d %4d %4d %4d %4d %4d\n",mem,State,changestate,dist,mem->StepTime[0],mem->StepTime[1],mem->StepTime[2],mem->StepTime[3],mem->StepTime[4],mem->StepTime[5]); if(changestate==3) { if((State==0 && dist>wi->RunHighTresh) || (State==1 && distRunLowTresh)) { State=1-State; changestate = 0; LastLeg = LastLeg%FootGroup[State]; for(i=0;iFootCount;i++) mem->StepTime[i]=StepSpeed[State]; NextLeg = bestleg%FootGroup[State]; } } bestleg = bestleg%FootGroup[State]; if(mem->Idle && changestate==1) NextLeg = bestleg; // do the next step mem->Idle = 0; if(changestate!=2 && mem->StepTime[LastLeg]>=StepNext[State] && mem->StepTime[NextLeg]>=StepSpeed[State]) { if(mem->InitSteps>0) { mem->InitSteps--; dist = 1024; } if(mem->DeviationFlag>0) mem->DeviationFlag--; if(dist>wi->StepTresh) { v.Sub3(dir.l,mat.l); f = v.Abs3(); if((wi->Flags & 1) && mem->InitSteps==0) NextLeg = bestleg; if(f>StepLength[State]) { mem->NewPos = mat.l; mem->NewPos.AddScale3(v,StepLength[State]/f); } else { mem->NewPos = dir.l; } // mem->NewPos.y = 0; dir.l = mem->NewPos; j = mem->StepTime[LastLeg]-StepNext[State]; for(i=NextLeg;iFootCount;i+=FootGroup[State]) { mem->FootOld[i] = foot[i];//mem->FootNew[i]; // sDPrintF("soll: %06.3f %06.3f %06.3f --\n ist: %06.3f %06.3f %06.3f\n",mem->FootOld[i].x,mem->FootOld[i].y,mem->FootOld[i].z,dir.l.x,dir.l.y,dir.l.z); v = mem->FootDelta[i]; v.y = 0; v.Rotate34(dir); if(moncell) { mem->FootNew[i] = v; // do the step in any case... mem->StepTime[i] = j; if(mem->FootCell[i]==0) mem->FootCell[i] = moncell; kenv->Game->CollisionForMonsterMode = 1; // and then find a better point-- p0 = v; p0.y += wi->scanup; p1 = v; p1.y -= wi->scandown; ci.Init(); if(kenv->Game->CollideRay(mem->FootCell[i],p1,p0,ci))//FindFirstIntersect(p0,p1,cell,&plane)) { if(ci.Plane.y>0.75 && ci.Pos.yFootNew[i] = ci.Pos; mem->StepTime[i] = j; } } kenv->Game->CollisionForMonsterMode = 0; } else { mem->FootNew[i] = v; mem->StepTime[i] = j; } } LastLeg = NextLeg; } else { mem->Idle = 1; } } // paint & cleanup matrix = mat; mem->State = State; mem->LastLeg = LastLeg; mem->NextLeg = NextLeg; } void KKriegerShot::Exit() { if(Event) { Event->Exit(); delete Event; } } sBool KKriegerGame::ShotAI(KKriegerShot *shot) { KKriegerCollideInfo ci; sVector newpos,speed; sMatrix mat; KEvent *event; sF32 d; sVector dir; ci.Init(1<<(KPK_WEAPON0+shot->Weapon),shot->Who); speed.Sub3(shot->Matrix.l,shot->OldPos); if(shot->Mode==2) speed.Scale3(0.999f); newpos.Add3(speed,shot->Matrix.l); newpos.w = 1; if(shot->Mode==2) { newpos.y -= 0.00025f; mat.InitRot(shot->Tensor); shot->Tensor.Scale3(0.990f); shot->Matrix.Mul3(mat); } shot->OldPos = shot->Matrix.l; shot->Matrix.l = newpos; if(CollideRay(shot->Cell,shot->Matrix.l,shot->OldPos,ci)) { if(shot->Mode==2 && !shot->DoubleKill) { shot->Matrix.l = ci.Pos; shot->Matrix.l.AddScale3(ci.Plane,0.125f); shot->DoubleKill=1; dir = shot->Tensor; shot->Tensor.Cross3(dir,ci.Plane); dir.Cross3(speed,ci.Plane); shot->Tensor.AddScale3(dir,-0.5f); speed.Scale3(0.75f); shot->OldPos.Sub3(shot->Matrix.l,speed); d = speed.Dot3(ci.Plane); shot->OldPos.AddScale3(ci.Plane,d*2.0f); } else { shot->Matrix.l = ci.Pos; event = new KEvent; event->Init(); event->Op = WeaponExplode[0][shot->Weapon]; event->Matrix.InitDir(ci.Plane); event->Matrix.l = shot->Matrix.l; Environment->AddDynamicEvent(event); return sFALSE; } } shot->DoubleKill = 0; if(ci.DCell && ci.DCell->Monster && shot->Who==KCRF_ISPLAYER) { ci.DCell->Monster->Hit(WeaponHits[shot->Weapon],this); event = new KEvent; event->Init(); event->Op = WeaponExplode[1][shot->Weapon]; event->Matrix = shot->Matrix; Environment->AddDynamicEvent(event); return sFALSE; } if(ci.DCell && ci.DCell==&Player.HitCell && shot->Who==KCRF_ISMONSTER) { Player.Hit(WeaponHits[shot->Weapon]); return sFALSE; } return sTRUE; } void KKriegerGame::MonsterAI(KKriegerMonster *mon,KEnvironment *kenv,sBool machinelife,sBool prevlife) { sF32 dist; sVector delta; sVector pos; sVector speed; sVector v; KKriegerCellAdd *cadd; // KKriegerCellAdd *cell; sVector p0,p1; sF32 attackdist; sBool spawn; KKriegerCollideInfo ci; // sMatrix mat; // sInt i; // kill monster if(mon->Life<=0) { mon->State = KMS_IDLE; mon->Life = 0; mon->DeathEvent.Matrix = mon->Matrix; return; } // if no player collision, do nothing if(Player.Collider.Cell==0) return; // spawn monster if(mon->State==KMS_IDLE) { spawn = 1; // normally, all monsters spawn immediatly if((mon->Flags&KMF_WHENPREVDEAD) && prevlife) spawn = 0; // spawning not allowed when previous is alife if((mon->Flags&KMF_WHENMACHINELIVES) && !machinelife) spawn = 0; // spawning not allowed when machine is dead if(!kenv->Game->Switches[mon->SpawnSwitch]) spawn = 0; // spawning not allowed by switch if(spawn) mon->State = KMS_SPAWNED; } // calculate distance if(mon->State!=KMS_IDLE) { pos = mon->Collider.Pos; delta.Sub3(pos,Player.Collider.Pos); delta.y = 0; dist = delta.UnitAbs3(); } // monster gets in range if(mon->State==KMS_SPAWNED && distNoticeRadius) { // cell = FindCell(mon->Collider.Pos); // if(cell) { mon->State=KMS_INRANGE; } } // monster goes out of range if((mon->State==KMS_INRANGE || mon->State==KMS_ACTIVE) && dist>mon->NoticeRadius*1.5f) mon->State=KMS_SPAWNED; // if monster sees player, it gets active. // it is not wise to deactivae monster when it looses the line of sight. // monsters also get active when shot at. if(mon->State==KMS_INRANGE) { p0 = Player.Collider.Pos; p1 = mon->Collider.Pos; p0.y += 1.5f; p1.y += 1.5f; cadd = PlayerCell; ci.Init(); if(!CollideRay(cadd,p1,p0,ci)) mon->State=KMS_ACTIVE; } // now the real monster AI if(mon->State==KMS_ACTIVE) { mon->MeeleeTimer += 0.01f/mon->MeeleeTime; if(mon->MeeleeTimer>=1.0f) mon->MeeleeTimer = 1.0f; mon->RangedTimer += 0.01f/mon->RangedTime; if(mon->RangedTimer>=1.0f) mon->RangedTimer = 1.0f; v.Sub3(mon->Collider.Pos,PlayerPos); attackdist = v.Dot3(v); if(attackdist < 1.5f*1.5f) { if(mon->RangedTimer==1.0f && mon->MeeleeTimer==1.0f && mon->MeeleeHits>0) { mon->MeeleeTimer = 0.0f; Player.Hit(mon->MeeleeHits); } } else { if(mon->RangedTimer==1.0f && mon->MeeleeTimer==1.0f && mon->RangedHits>0 && mon->WeaponKind>=0) { sVector p0,p1; KKriegerCellAdd *cell; mon->RangedTimer = 0.0f; p0 = Player.Collider.Pos; p0.y += 1.5f; p1 = mon->Collider.Pos; p1.y += 1.5f; ci.Init(); cell = Player.Collider.Cell; if(!CollideRay(cell,p1,p0,ci)) { p0.Sub4(p1); p0.Unit3(); FireShot(kenv,mon->WeaponKind,mon,&p0); } } } if(distChargeRadius) { dist -= mon->MinRadius; dist = sRange(dist,mon->Speed,-mon->Speed); speed.Scale3(delta,-dist); } else { dist -= mon->WaitRadius; dist = sRange(dist,mon->Speed,-mon->Speed); speed.Scale3(delta,-dist); } if(!(mon->Flags & KMF_IMMOBILE) && mon->Collider.Cell) { sVector p0; sVector p1; sF32 d,testh,upforce; KKriegerCellAdd *cell; KKriegerCollideInfo ci; testh = StairHeight + GravityPlayer + mon->Collider.Radius; d = testh; ci.Init(0,KCRF_ISMONSTER); p0 = mon->Collider.Pos; p0.AddScale3(mon->Matrix.k,mon->Collider.Radius); p1 = p0; p1.y -= testh; cell = mon->Collider.Cell; v.Sub3(mon->Collider.Pos,mon->Collider.OldPos); speed.Add3(mon->MagnetForce); speed.y = 0; speed.w = 0; speed.AddScale3(v,0.9f); mon->HitCell.Mode |= KCM_DISABLED; if(CollideRay(cell,p1,p0,ci)) d += ci.Dist; mon->HitCell.Mode &= ~KCM_DISABLED; upforce = 0; if(dCollider.OldPos = mon->Collider.Pos; mon->Collider.OldPos.y += upforce; speed.y += upforce; MoveCollider(mon->Collider,speed,KCRF_ISMONSTER); // mon->Collider.Pos.Add3(speed); mon->Matrix.i.Init(-delta.z,0,delta.x,0); // make monster look to player mon->Matrix.j.Init(0,1,0,0); mon->Matrix.k.Init(-delta.x,0,-delta.z,0); mon->Matrix.l = mon->Collider.Pos; KKMonsterWalk(kenv,&mon->WalkMem,&KKMonsterWalkInfoTable[mon->WalkStyle],mon->Spline,mon->Matrix,mon->Collider.Cell); // mon->Matrix.l = mon->Collider.Pos; // mon->Collider.Pos = mon->Matrix.l; } if(mon->Collider.Cell==0) { mon->Hit(65536,kenv->Game); } } // set event for painting mon->Event.Matrix = mon->Matrix; mon->Event.Scale.x = mon->MeeleeTimer-0.5f; if(mon->Event.Scale.x<0) mon->Event.Scale.x+=1; mon->Event.Scale.y = mon->RangedTimer-0.5f; if(mon->Event.Scale.y<0) mon->Event.Scale.y+=1; mon->Event.Scale.z = 1.0f * mon->Life / mon->LifeMax; mon->Event.CullDist = 35; } // gegenseitige abstoßung der monster void KKriegerGame::MonsterMagnetAI() { KKriegerMonster *mons[256]; KKriegerMonster *mon0,*mon1; sInt count; sInt i,j; sF32 dist,radius; sVector v; // liste aller aktiven monster count = 0; for(i=0;iMagnetForce.Init(0,0,0,0); if(mon0->State==KMS_ACTIVE && count<256) mons[count++] = mon0; } // n^2/2 tests radius = 1.0f; for(i=0;iCollider.Pos,mon1->Collider.Pos); dist = v.Dot3(v); if(dist0.99f) v.InitRnd(); // vermeide einheitsvector von nullvector.. else v.Unit3(); mon0->MagnetForce.AddScale3(v,dist*0.001f); mon1->MagnetForce.AddScale3(v,dist*-0.001f); } } } } /****************************************************************************/ /*** ***/ /*** Particles and Collision ***/ /*** ***/ /****************************************************************************/ void KKriegerGame::FlushPhysic() { // sInt i; PartUsed = 0; ConsUsed = 0; DCellUsed = 0; Monsters.Count = 0; AddDCell(&Player.HitCell); } void KKriegerGame::AddDCell(KKriegerCellDynamic *d) { sVERIFY(DCellUsed < KKRIEGER_MAXDCELL); DCellPtr[DCellUsed++] = d; } /****************************************************************************/ void KKriegerGame::Physic(sF32 fade) { sInt i; KKriegerCellDynamic *cell; #if sPROFILE sZONE(GamePhysic); CallFindFirstIntersect = 0; CallFindSubIntersect = 0; CallMoveParticle = 0; CallOutsideMask = 0; CallIntersectOut = 0; CallIntersectIn = 0; #endif // dynamic cells for(i=0;iCurrentMatrix.Lin4(cell->Matrix0,cell->Matrix1,fade); cell->ApplyMatrix(cell->CurrentMatrix); cell->ConfirmedMatrix = cell->CurrentMatrix; cell->Blocked = 0; cell->Mode &= ~KCM_UNCONFIRMED; } } /****************************************************************************/ /*** ***/ /*** Totally new math ***/ /*** ***/ /****************************************************************************/ sBool KKriegerGame::CollideRay( KKriegerCellAdd *&cadd, // old / new add-cell const sVector &p1, // move to here const sVector &p0, // move from here KKriegerCollideInfo &ci) // collide info, or 0 { sInt i,j; sVector v; KKriegerCellAdd *list[16]; KKriegerCollideInfo ci2; sInt count; sVector d; sBool isnotplayer; GAMEPERF(CallFindFirstIntersect); list[0] = cadd; count = 1; isnotplayer = !(ci.Flags&KCRF_ISPLAYER); d.Sub4(p1,p0); again: // see if we leave this cadd if(cadd->IntersectOut(p1,d,ci.Plane,ci.Dist)) { v = p1; v.AddScale3(d,ci.Dist); ci.Pos = v; if(count<16) { for(i=0;iAdds.Count;i++) { sVERIFY(cadd->Adds[i]!=cadd); if(cadd->Adds[i]->OutsideMask(v)==0) { for(j=0;jAdds[i]) goto retry; cadd = cadd->Adds[i]; list[count++] = cadd; goto again; } retry:; } } else { #if LOGGING sDPrintF("FindFirstIntersect(): to many retries\n"); #endif } // we collided with the outside of an add, even after following all // connected adds. we have a list of all visited adds in (list,count) // and we need to check them for a collision that might be before the // collision with the add's outside wall. ci2 = ci; if(CollideRaySub(d,p1,p0,list,count,ci2)) { if(ci2.DistSubs.Count;i++) { cell = cadd->Subs[i]; if((cell->Mode&3)==KCM_SUB) { if(CollideRaySub2(d,p1,p0,cell,ci)) { collided = 1; ci.DCell = 0; // if(SomethingBadHasHappened) goto fatal; } } } for(i=0;iZones.Count;i++) { cell = cadd->Zones[i]; hit0 = (cell->OutsideMask(p0)==0); hit1 = (cell->OutsideMask(p1)==0); if(hit0 && !hit1) cell->Leave |= ci.ZoneMask; if(!hit0 && hit1) cell->Enter |= ci.ZoneMask; if(hit1) cell->Collided |= ci.ZoneMask; } } for(i=0;iMode&3)==KCM_SUB) { if(CollideRaySub2(d,p1,p0,cell,ci)) { collided = 1; ci.DCell = dcell; // if(SomethingBadHasHappened) goto fatal; } } if((cell->Mode&3)==KCM_ZONE) { hit0 = (cell->OutsideMask(p0)==0); hit1 = (cell->OutsideMask(p1)==0); if(hit0 && !hit1) cell->Leave |= ci.ZoneMask; if(!hit0 && hit1) cell->Enter |= ci.ZoneMask; if(hit1) { cell->Collided |= ci.ZoneMask; ci.DCell = dcell; } } } //fatal: return collided; } sBool KKriegerGame::CollideRaySub2(const sVector &d,const sVector &p1,const sVector &p0,KKriegerCell *cell,KKriegerCollideInfo &ci) { sF32 besti; sVector v,planei; sBool collided; sF32 distresult; // find any rejection plane if(cell->RejectPlane(p0,p1)) return 0; // if there's none, we have a collision collided = 0; distresult = ci.Dist; if(cell->IntersectIn(p1,d,planei,besti,ci.Radius)) { if(bestiOutsideMask(v,ci.Radius-EPSILON)==0) { distresult = besti; collided = 1; ci.Dist = distresult; ci.Plane = planei; ci.Plane.Neg4(); ci.Pos = p1; ci.Pos.AddScale3(d,distresult); } } } return collided; } /****************************************************************************/ /*** ***/ /*** New Math ***/ /*** ***/ /****************************************************************************/ // calculate which planes are out // positive epsilons will enlarge the box! sBool KKriegerCell::RejectPlane(const sVector &v0,const sVector &v1) { sInt i; for(i=0;i cell when casting from inside to outside sBool KKriegerCell::IntersectOut(const sVector &p,const sVector &d,sVector &plane,sF32 &distresult,sF32 radius) { sInt bestplane; sInt i; sF32 dist,det; sF32 best; GAMEPERF(KKriegerGame::CallIntersectOut); best = 0; bestplane = -1; for(i=0;i-1.0e-16) { #if LOGGING sDPrintF("KKriegerCell::IntersectOut() Panic\n"); #endif // return sFALSE; } dist = -dist/det; if(dist=0) { plane = Planes[bestplane]; distresult = best; return sTRUE; } return sFALSE; } // calculate intersection point ray -> cell when casting from the outside sBool KKriegerCell::IntersectIn(const sVector &p,const sVector &d,sVector &plane,sF32 &distresult,sF32 radius) { sInt bestplane; sInt i; sF32 dist,det; sF32 best; GAMEPERF(KKriegerGame::CallIntersectIn); best = -1024*1024; bestplane = -1; for(i=0;i1.0e-16) { dist = -dist/det; if(dist>best) { best = dist; bestplane = i; } } else { #if LOGGING sDPrintF("KKriegerCell::IntersectIn() Panic\n"); #endif } } if(bestplane>=0) { plane = Planes[bestplane]; distresult = best; return sTRUE; } return sFALSE; } /****************************************************************************/ // find an ADD cell. KKriegerCellAdd *KKriegerGame::FindCell(const sVector &v) { sInt i; for(i=0;iOutsideMask(v)==0) return CellAdd[i]; } return 0; } // faked code to check CollideRay sBool KKriegerGame::MoveColliderX(KKSolidCollider &collider, const sVector &v,sInt who) { KKriegerCollideInfo ci; KKriegerCellAdd *cell; sVector old; ci.Init((who&KCRF_ISPLAYER)?KPM_PLAYER:0,who); ci.Radius = collider.Radius; old = collider.Pos; collider.Pos.Add3(v); if(CollideRay(collider.Cell,collider.Pos,old,ci)) { collider.Pos = ci.Pos; } if(collider.Cell->OutsideMask(collider.Pos,0)!=0) { cell = FindCell(collider.Pos); if(cell) { collider.Cell = cell; sDPrintF("Collision: Rescued Desaster\n"); } else { sDPrintF("Collision: Lasting Desaster\n"); } } return sFALSE; } // cool code that does not work sBool KKriegerGame::MoveCollider(KKSolidCollider &collider, const sVector &v,sInt who) { sVector tmp; KKriegerCellAdd *cell; KKriegerCellAdd *list[16]; sInt count; sVector sphere; sInt i; tmp = collider.Pos; collider.Pos.Add3(v);//collider.Movement); sphere = collider.Pos; sphere.w = collider.Radius; count = 1; list[0] = collider.Cell; CollideSoftSphereAdd(sphere, list, count); for(i = 0; i < DCellUsed; i++) { if((who & KCRF_ISMONSTER) && DCellPtr[i]->Monster) continue; if((who & KCRF_ISPLAYER) && DCellPtr[i] == &Player.HitCell) continue; CollideSoftSphereSub(sphere, DCellPtr[i]); } collider.Pos = sphere; collider.Pos.w = 1.0f; // update cell (there might be a cheaper way...) KKriegerCollideInfo ci; ci.Init((who&KCRF_ISPLAYER)?KPM_PLAYER:0,who); CollideRay(collider.Cell,collider.Pos,tmp,ci); if(collider.Cell->OutsideMask(collider.Pos,0)!=0) { cell = FindCell(collider.Pos); if(cell) { collider.Cell = cell; sDPrintF("Collision: Rescued Desaster\n"); } else { sDPrintF("Collision: Lasting Desaster\n"); } } return sFALSE; } void KKriegerGame::CollideSoftSphereAdd(sVector &sphere, KKriegerCellAdd **cellList, sInt &count) { KKriegerCellAdd *cell; int i, j; cell = cellList[count-1]; if(sFAbs(cell->approxDistance(sphere)) < sphere.w) { GenSimpleFaceList *face; face = cell->Faces; while(face != 0) { CollideSoftSphereFace(sphere, face->Vertices, face->VertexCount); face = face->Next; } } for(i = 0; i < cell->Adds.Count; i++) { if(cell->Adds[i]->approxDistance(sphere) < sphere.w) { for(j = 0; j < count; j++) if(cell->Adds[i] == cellList[j]) break; if(j == count && count < 16) { cellList[count++] = cell->Adds[i]; CollideSoftSphereAdd(sphere, cellList, count); } } } for(i = 0; i < cell->Subs.Count; i++) if(cell->Subs[i]->approxDistance(sphere) < sphere.w) CollideSoftSphereSub(sphere, cell->Subs[i]); } void KKriegerGame::CollideSoftSphereSub(sVector &sphere, KKriegerCell *cell) { sVector plane[4]; static const sInt order[6][4] = { { 0,2,3,1 }, { 2,6,7,3 }, { 3,7,5,1 }, { 4,0,1,5 }, { 2,0,4,6 }, { 7,6,4,5 }, }; int i; for(i = 0; i < 6; i++) { plane[0] = cell->Vertices[order[i][0]]; plane[1] = cell->Vertices[order[i][1]]; plane[2] = cell->Vertices[order[i][2]]; plane[3] = cell->Vertices[order[i][3]]; CollideSoftSphereFace(sphere, plane, 4); } } sF32 KKriegerCell::approxDistance(const sVector &p) { sInt i; sF32 result, distance; result = 10000; for(i = 0; i < PlaneCount; i++) { distance = p.Dot3(Planes[i]) + Planes[i].w; if(distance < result) result = distance; } return -result; } void KKriegerGame::CollideSoftSphereFace(sVector &sphere, const sVector *vertices, int numVertices) { sVector collPoint, shift; sF32 length, distance; if(CheckSphereVsFace(sphere, vertices, numVertices, collPoint)) { shift.Sub3(sphere, collPoint); length = shift.Abs3(); distance = (sphere.w - length) * 0.5f; sphere.AddScale3(shift, distance / length); sphere.w -= distance; } } sBool KKriegerGame::CheckSphereVsFace(const sVector &sphere, const sVector *vertices, int numVertices, sVector &nearestPoint) { sVector normal; sVector a, b, toSphere; sVector tmp2, tmp; sVector sphereCopy; sF32 distance; sInt i; sBool result, inside; a.Sub3(vertices[2], vertices[1]); b.Sub3(vertices[0], vertices[1]); normal.Cross3(a, b); if(normal.Abs3() <= EPSILON) return false; normal.Unit3(); toSphere.Sub3(sphere, vertices[0]); distance = normal.Dot3(toSphere); // early out if sphere to far from plane if(sFAbs(distance) > sphere.w) return sFALSE; // now test for each edge whether this point lies inside the face // if it lies outside, check for collision with the edge sphereCopy = sphere; result = sFALSE; inside = sTRUE; for(i = 0; i < numVertices; i++) { tmp.Sub3(vertices[(i+1) % numVertices], vertices[i]); tmp2.Cross3(tmp, normal); tmp.Sub3(sphere, vertices[i]); if(tmp.Dot3(tmp2) > 0) { inside = sFALSE; // check edge if(CheckSphereVsEdge(sphereCopy, vertices[i], vertices[(i+1) % numVertices], nearestPoint)) { result = sTRUE; sphereCopy.w = sphereCopy.Distance(nearestPoint); } } } if(inside == sFALSE) return result; // just find and return the nearest point on the plane nearestPoint.Scale3(normal, -distance); nearestPoint.Add3(sphere); return sTRUE; } sBool KKriegerGame::CheckSphereVsEdge(const sVector &sphere, const sVector &v1, const sVector &v2, sVector &nearestPoint) { sVector unit, toSphere; sF32 a, length; unit.Sub3(v2, v1); length = unit.Abs3(); if(length < EPSILON) return false; unit.Scale3(1.0f / length); toSphere.Sub3(sphere, v1); a = unit.Dot3(toSphere); if(a < 0) { // test vertice 1 if(toSphere.Abs3() > sphere.w) return sFALSE; nearestPoint = v1; return sTRUE; } if(a > length) // the second vertice is tested as the first one of the next edge if appropriate return sFALSE; nearestPoint.Scale3(unit, a); nearestPoint.Add3(v1); toSphere.Sub3(sphere, nearestPoint); if(toSphere.Abs3() > sphere.w) return sFALSE; return sTRUE; } #define SPLITHEAPMAX 1024 sVector SplitHeapVector[2][SPLITHEAPMAX]; sInt SplitHeapIndex[2]; void KKriegerGame::CreateCollisionFaces(KKriegerCellAdd &add) { GenSimpleFaceList *cur, *next; int i; static const sInt order[6][4] = { { 0,2,3,1 }, { 2,6,7,3 }, { 3,7,5,1 }, { 4,0,1,5 }, { 2,0,4,6 }, { 7,6,4,5 }, }; // add original faces add.DeleteFaces(); for(i = 0; i < 6; i++) { cur = new GenSimpleFaceList; cur->Init(4); cur->Vertices[0] = add.Vertices[order[i][0]]; cur->Vertices[1] = add.Vertices[order[i][1]]; cur->Vertices[2] = add.Vertices[order[i][2]]; cur->Vertices[3] = add.Vertices[order[i][3]]; cur->Next = add.Faces; add.Faces = cur; } for(i = 0; i < add.Adds.Count; i++) { cur = add.Faces; add.Faces = 0; while(cur != 0) { next = cur->Next; SplitHeapIndex[0] = 0; SplitHeapIndex[1] = 0; if(SplitCollisionFace(*cur, add.Adds[i]->Planes, 0, add.Adds[i]->PlaneCount, add)) { cur->Next = add.Faces; add.Faces = cur; } else { cur->Exit(); delete cur; } sVERIFY(SplitHeapIndex[0]==0); sVERIFY(SplitHeapIndex[1]==0); cur = next; } } } sBool KKriegerGame::SplitCollisionFace(const GenSimpleFace &face, const sVector *planes, sInt plane, sInt nPlanes, KKriegerCellAdd &add) { GenSimpleFace sides[2]; sBool use[2]; sInt i, j; sVector center; sVector normal; // debug_count_splits++; if(nPlanes == 0) return sTRUE; { sVector a, b; a.Sub3(face.Vertices[2], face.Vertices[1]); b.Sub3(face.Vertices[0], face.Vertices[1]); normal.Cross3(a, b); } sides[0].Vertices = &SplitHeapVector[0][SplitHeapIndex[0]]; sides[1].Vertices = &SplitHeapVector[1][SplitHeapIndex[1]]; face.Clip(planes[plane], sides); SplitHeapIndex[0] += sides[0].VertexCount; SplitHeapIndex[1] += sides[1].VertexCount; sVERIFY(SplitHeapIndex[0] 2; if(use[i] && plane == nPlanes - 1) { use[i] = sFALSE; center.Init3(); for(j = 0; j < sides[i].VertexCount; j++) center.Add3(sides[i].Vertices[j]); center.Scale3(1.0f / sides[i].VertexCount); for(j = 0; j <= nPlanes; j++) { float d = planes[j].Dot3(center) + planes[j].w; if(d < 0 || (d < EPSILON*4 && planes[j].Dot3(normal) > 0)) use[i] = sTRUE; } } if(use[i] && plane < nPlanes - 1) use[i] = SplitCollisionFace(sides[i], planes, plane + 1, nPlanes, add); } if(!(use[0] && use[1])) { for(i = 0; i < 2; i++) { if(use[i]) { GenSimpleFaceList *NewFace = new GenSimpleFaceList; NewFace->Init(sides[i].VertexCount); for(j = 0; j < sides[i].VertexCount; j++) NewFace->Vertices[j] = sides[i].Vertices[j]; NewFace->Next = add.Faces; add.Faces = NewFace; } } } SplitHeapIndex[0] -= sides[0].VertexCount; SplitHeapIndex[1] -= sides[1].VertexCount; // sides[0].Exit(); // sides[1].Exit(); return use[0] && use[1]; } void KKriegerCellAdd::DeleteFaces() { GenSimpleFaceList *cur, *next; cur = Faces; while(cur != 0) { next = cur->Next; cur->Exit(); delete cur; cur = next; } } /****************************************************************************/ /*** ***/ /*** Operators ***/ /*** ***/ /****************************************************************************/ #pragma lekktor(on) void __stdcall Exec_KKrieger_Para(KOp *op,KEnvironment *kenv) { sU32 *du; sF32 *df; sInt i; du = op->GetAnimPtrU(0); df = op->GetAnimPtrF(0); kenv->Game->Player.LifeMax = du[1]; kenv->Game->Player.ArmorMax = du[3]; kenv->Game->Player.AlphaMax = du[5]; kenv->Game->Player.BetaMax = du[7]; kenv->Game->Player.AmmoMax[0] = du[9]; kenv->Game->Player.AmmoMax[1] = du[11]; kenv->Game->Player.AmmoMax[2] = du[13]; kenv->Game->Player.AmmoMax[3] = du[15]; if(kenv->Game->ResetByOp) { kenv->Game->ResetByOp = 0; kenv->Game->Player.Life = du[0]; kenv->Game->Player.Armor = du[2]; kenv->Game->Player.Alpha = du[4]; kenv->Game->Player.Beta = du[6]; kenv->Game->Player.Ammo[0] = du[8]; kenv->Game->Player.Ammo[1] = du[10]; kenv->Game->Player.Ammo[2] = du[12]; kenv->Game->Player.Ammo[3] = du[14]; for(i=0;i<8;i++) kenv->Game->Player.Weapon[i] = (du[16]>>i)&1;; } kenv->Game->GravityPlayer = df[17]; kenv->Game->DampPlayerGround = df[18]; kenv->Game->DampPlayerAir = df[19]; kenv->Game->DampPlayerHeight = df[20]; kenv->Game->StairHeight = df[21]; kenv->Game->EyeHeight = df[22]; kenv->Game->MouseTurnSpeed = df[23]; kenv->Game->MouseLookSpeed = df[24]; kenv->Game->AccelSideFactor = df[25]; kenv->Game->AccelForwFactor = df[26]; kenv->Game->AccelBackFactor = df[27]; kenv->Game->JumpForce = df[28]; kenv->Game->PlayerStartPos.Init(df[29],df[30],df[31]); kenv->Game->DampPlayerCam = df[56]; sCopyMem(kenv->Game->WeaponHits,du+32,24*4); kenv->Game->CamOffset = kenv->Game->EyeHeight-kenv->Game->StairHeight; op->ExecInputs(kenv); } void __stdcall Exec_KKrieger_Events(KOp *op,KEnvironment *kenv) { KOp **ptr; sInt i; switch(*op->GetAnimPtrU(0)) { case 0: default: ptr = kenv->Game->WeaponShot; break; case 1: ptr = kenv->Game->WeaponOptics; break; case 2: ptr = kenv->Game->WeaponExplode[0]; break; case 3: ptr = kenv->Game->WeaponExplode[1]; break; } for(i=0;i<8;i++) ptr[i] = op->GetLink(i); op->ExecInputs(kenv); } class KObject * __stdcall Init_KKrieger_Para(KOp *op,KEnvironment *kenv) { sF32 *df; df = op->GetAnimPtrF(0); kenv->Game->PlayerStartPos.Init(df[29],df[30],df[31]); if(op->GetInput(0)) return op->GetInput(0)->Cache; else return new GenScene; } class KObject * __stdcall Init_KKrieger_Events(KOp *op) { if(op->GetInput(0)) return op->GetInput(0)->Cache; else return new GenScene; } /****************************************************************************/ struct MonsterMem : public KInstanceMem { KKriegerMonster Monster; }; void __stdcall Exec_KKrieger_Monster(KOp *op,KEnvironment *kenv) { KKriegerMonster *mon; sF32 *fp; sInt *ip; //sMatrix oldmat; sMatrix mat; sVector scale,delta; // KKriegerCellAdd *cell; sInt i; MonsterMem *mem; mem = kenv->GetInst(op); mon = &mem->Monster; if(mem->Reset) { sSetMem(mon,0,sizeof(KKriegerMonster)); mem->DeleteChild = &mon->Event.FirstInstance; mem->DeleteChild2 = &mon->DeathEvent.FirstInstance; } /* if(mon->Life==-1) { mon->Event.FirstInstance->DeleteChain(); mon->Event.FirstInstance = 0; } */ if(mem->Reset || mon->State==KMS_RESPAWN) { fp = op->GetAnimPtrF(0); ip = op->GetAnimPtrS(0); mon->Event.Init(); sCopyMem(&mon->Speed ,fp+ 6,16*4); mon->Speed = mon->Speed/1000; mon->WalkStyle = ip[26]; mon->WeaponKind = ip[27]; mon->Spline = op->GetSpline(0); mon->Life = mon->LifeMax; mon->State = KMS_IDLE; mon->SpawnSwitch = ip[28]; mon->Event.Op = 0; mon->Event.Monster = mon; mon->DeathEvent.Init(); mon->PosOffset.Init4(fp[23],fp[24],fp[25],0); mon->Matrix = kenv->ExecStack.Top(); delta.Sub3(mon->Matrix.l,kenv->Game->Player.Collider.Pos); delta.y = 0; delta.Unit3(); mon->Matrix.i.Init(-delta.z,0,delta.x,0); // make monster look to player mon->Matrix.j.Init(0,1,0,0); mon->Matrix.k.Init(-delta.x,0,-delta.z,0); mon->Collider.Pos = mon->Matrix.l; mon->Collider.Cell = kenv->Game->FindCell(mon->Collider.Pos); mon->Collider.Radius = 0.25f; mon->Collider.OldPos = mon->Collider.Pos; if(mon->Collider.Cell==0) mon->Life = 0; scale.Init(fp[20],fp[21],fp[22]); mat.Init(); mat.l.Add3(mon->PosOffset); mon->HitCell.Init(mat,scale,KCM_ZONE); mat = mon->Matrix; mon->HitCell.PrepareDynamic(mat); mon->HitCell.Monster = mon; mon->WalkMem.Reset = 1; mon->Matrix.l = mon->Collider.Pos; KKMonsterWalk(kenv,&mon->WalkMem,&KKMonsterWalkInfoTable[mon->WalkStyle],mon->Spline,mon->Matrix,mon->Collider.Cell); if(kenv->NextMonsterOverride) { mon->KillSwitch = kenv->NextMonsterKillSwitch; mon->SpawnSwitch = kenv->NextMonsterSpawnSwitch; mon->Flags |= kenv->NextMonsterFlags; kenv->NextMonsterOverride = 0; } } sVERIFY(op->GetInput(0)); if(mon->Life>0) { mon->HitCell.Matrix1 = mon->Event.Matrix; *kenv->Game->Monsters.Add() = mon; if(mon->State>=KMS_INRANGE) { kenv->Game->AddDCell(&mon->HitCell); kenv->ExecStack.PushIdentity(); for(i=0;i<8;i++) kenv->Var[KV_LEG_L0+i] = mon->WalkMem.LegPosOut[i]; kenv->Var[KV_LEG_TIMES].x = mon->WalkMem.LegTimeOut; op->GetInput(0)->ExecEvent(kenv,&mon->Event,1); kenv->ExecStack.Pop(); } } else { if(op->GetInput(1)) { kenv->ExecStack.PushIdentity(); mon->DeathEvent.Matrix = mon->Event.Matrix; op->GetInput(1)->ExecEvent(kenv,&mon->DeathEvent,1); kenv->ExecStack.Pop(); } } } class KObject * __stdcall Init_KKrieger_Monster(KOp *op) { sInt i; for(i=0;iGetInputCount();i++) { sVERIFY(op->GetInput(i)); sVERIFY(op->GetInput(i)->Cache); op->GetInput(i)->Cache->Release(); } return new GenScene(); } /****************************************************************************/ void __stdcall Exec_KKrieger_MonsterFlags(KOp *op,KEnvironment *kenv,sInt kill,sInt spawn,sInt flags) { kenv->NextMonsterOverride = 1; kenv->NextMonsterKillSwitch = kill; kenv->NextMonsterSpawnSwitch = spawn; kenv->NextMonsterFlags = flags; op->ExecInputs(kenv); kenv->NextMonsterOverride = 0; } class KObject * __stdcall Init_KKrieger_MonsterFlags(KOp *op) { sVERIFY(op->GetInput(0)); sVERIFY(op->GetInput(0)->Cache); return op->GetInput(0)->Cache; // op->GetInput(0)->Cache->Release(); // return new GenScene(); } /****************************************************************************/ class KObject * __stdcall Init_KKrieger_Respawn(sF323 pos,sInt zone,sF32 dir) { return new GenScene; } void __stdcall Exec_KKrieger_Respawn(KOp *op,KEnvironment *kenv,sF323 pos,sInt zone,sF32 dir) { kenv->Game->Respawn[zone].Valid = 1; kenv->Game->Respawn[zone].Pos.Init(pos.x,pos.y,pos.z); kenv->Game->Respawn[zone].Dir = dir*sPI2F; } /****************************************************************************/ class KObject * __stdcall Init_KKrieger_SplashDamage(sInt hits,sF32 range,sF32 enable) { return new GenScene; } struct SplashDamageMem : public KInstanceMem { sInt old; }; void __stdcall Exec_KKrieger_SplashDamage(KOp *op,KEnvironment *kenv,sInt hits,sF32 range,sF32 enable) { SplashDamageMem *mem; sBool ok; mem = kenv->GetInst(op); ok = (enable>0); if(ok && !mem->old) { kenv->Game->SplashEnable = 1; kenv->Game->SplashPos = kenv->ExecStack.Top().l; kenv->Game->SplashRange = range; kenv->Game->SplashHits = hits; } mem->old = ok; } /****************************************************************************/ /*** ***/ /*** Helpers ***/ /*** ***/ /****************************************************************************/ static sBool sMakePlane(sVector &p,const sVector &v0,const sVector &v1,const sVector &v2) { sVector d0,d1,v; sF64 e; d0.Sub3(v0,v1); d1.Sub3(v1,v2); v.Cross3(d0,d1); if(v.Dot3(v)<1e-10) return sFALSE; e = sFSqrt(v.Dot3(v)); // we need some really normal normals here v.x = v.x/e; v.y = v.y/e; v.z = v.z/e; v.w = v.Dot3(v1); p = v; return sTRUE; } void KKriegerCell::Init(const sMatrix &mat,const sVector &scale,sInt mode) { sVector va[8]; sInt i; for(i=0;i<8;i++) { va[i].x = scale.x * -(((i>>0)&1)-0.5f); va[i].y = scale.y * -(((i>>1)&1)-0.5f); va[i].z = scale.z * -(((i>>2)&1)-0.5f); va[i].w = 1; va[i].Rotate34(mat); } Init(va,mode); } void KKriegerCell::Init(const sVector *va,sInt mode) { sInt k; sF32 d; sVector p; sBool ok; static sInt order[6][4] = { { 0,2,3,1 }, { 2,6,7,3 }, { 3,7,5,1 }, { 4,0,1,5 }, { 2,0,4,6 }, { 7,6,4,5 }, }; sSetMem(this,0,sizeof(*this)); for(k=0;k<8;k++) Vertices[k] = va[k]; Mode = mode&3;//(3|8); OnlyForShot = (mode&4)?1:0; // NotForPlayer = (mode&KCM_NOTFORPLAYER)?1:0; for(k=0;k<6;k++) { ok = sFALSE; if(!sMakePlane(p,va[order[k][0]],va[order[k][1]],va[order[k][2]])) { if(!sMakePlane(p,va[order[k][1]],va[order[k][2]],va[order[k][3]])) if(!sMakePlane(p,va[order[k][2]],va[order[k][3]],va[order[k][0]])) if(!sMakePlane(p,va[order[k][3]],va[order[k][0]],va[order[k][1]])) ; // no way finding even a single plane, no else ok = sTRUE; // it's just a triange else ok = sTRUE; // it's just a triange else ok = sTRUE; // it's just a triange } else // it's a quad. lets check if it's a plane one! { ok = sTRUE; d = p.Dot3(va[order[k][3]])-p.w; if(d<-0.005f) // it's a bad one, and even inconvex { sMakePlane(p,va[order[k][1]],va[order[k][2]],va[order[k][3]]); sVERIFY(PlaneCount<=KKRIEGER_MAXPLANES); Planes[PlaneCount++] = p; sMakePlane(p,va[order[k][3]],va[order[k][0]],va[order[k][1]]); } else if(d>0.005f) // it's a bad one, but convex { sVERIFY(PlaneCount<=KKRIEGER_MAXPLANES); Planes[PlaneCount++] = p; sMakePlane(p,va[order[k][2]],va[order[k][3]],va[order[k][0]]); } } if(ok) { sVERIFY(PlaneCount<=KKRIEGER_MAXPLANES); Planes[PlaneCount++] = p; } } for(k=0;kFirstInstance) Event[1]->FirstInstance->DeleteChain(); Event[1]->FirstInstance = 0; } } /****************************************************************************/ void KKriegerCellDynamic::PrepareDynamic(sMatrix &mat) { sInt i; for(i=0;i