diff --git a/dumb/CMakeLists.txt b/dumb/CMakeLists.txt index 2b70ee412..9c1a69a79 100644 --- a/dumb/CMakeLists.txt +++ b/dumb/CMakeLists.txt @@ -3,6 +3,7 @@ cmake_minimum_required( VERSION 2.4 ) make_release_only() include( CheckFunctionExists ) +include( CheckCXXCompilerFlag ) # DUMB is much slower in a Debug build than a Release build, so we force a Release # build here, since we're not maintaining DUMB, only using it. @@ -104,5 +105,9 @@ add_library( dumb target_link_libraries( dumb ) if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) - set_source_files_properties( src/it/filter.cpp PROPERTIES COMPILE_FLAGS -msse ) + CHECK_CXX_COMPILER_FLAG( -msse DUMB_CAN_USE_SSE ) + + if( DUMB_CAN_USE_SSE ) + set_source_files_properties( src/it/filter.cpp PROPERTIES COMPILE_FLAGS -msse ) + endif( DUMB_CAN_USE_SSE ) endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 393f50905..2651308fd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -28,7 +28,9 @@ endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) option( DYN_FLUIDSYNTH "Dynamically load fluidsynth" ON ) -option( OSX_COCOA_BACKEND "Use native Cocoa backend instead of SDL" ON ) +if( APPLE ) + option( OSX_COCOA_BACKEND "Use native Cocoa backend instead of SDL" ON ) +endif( APPLE ) if( CMAKE_SIZEOF_VOID_P MATCHES "8" ) set( X64 64 ) @@ -590,6 +592,7 @@ set( PLAT_SDL_SYSTEM_SOURCES sdl/i_cd.cpp sdl/i_main.cpp sdl/i_movie.cpp + sdl/i_steam.cpp sdl/i_system.cpp sdl/sdlvideo.cpp sdl/sdlglvideo.cpp diff --git a/src/actor.h b/src/actor.h index e80cdb227..7651c6ff6 100644 --- a/src/actor.h +++ b/src/actor.h @@ -347,6 +347,14 @@ enum MF7_DONTTHRUST = 0x00000100, // Thrusting functions do not take, and do not give thrust (damage) to actors with this flag. MF7_ALLOWPAIN = 0x00000200, // Invulnerable or immune (via damagefactors) actors can still react to taking damage even if they don't. MF7_CAUSEPAIN = 0x00000400, // Damage sources with this flag can cause similar effects like ALLOWPAIN. + MF7_THRUREFLECT = 0x00000800, // Actors who are reflective cause the missiles to not slow down or change angles. + MF7_MIRRORREFLECT = 0x00001000, // Actor is turned directly 180 degrees around when reflected. + MF7_AIMREFLECT = 0x00002000, // Actor is directly reflected straight back at the one who fired the projectile. + MF7_HITTARGET = 0x00004000, // The actor the projectile dies on is set to target, provided it's targetable anyway. + MF7_HITMASTER = 0x00008000, // Same as HITTARGET, except it's master instead of target. + MF7_HITTRACER = 0x00010000, // Same as HITTARGET, but for tracer. + + // --- mobj.renderflags --- @@ -859,7 +867,7 @@ public: DWORD flags4; // [RH] Even more flags! DWORD flags5; // OMG! We need another one. DWORD flags6; // Shit! Where did all the flags go? - DWORD flags7; // + DWORD flags7; // WHO WANTS TO BET ON 8!? // [BB] If 0, everybody can see the actor, if > 0, only members of team (VisibleToTeam-1) can see it. DWORD VisibleToTeam; diff --git a/src/b_bot.cpp b/src/b_bot.cpp index d2acdcab6..05cac045b 100644 --- a/src/b_bot.cpp +++ b/src/b_bot.cpp @@ -66,7 +66,7 @@ void DBot::Serialize (FArchive &arc) arc << savedyaw << savedpitch; } - else if (SaveVersion >= 4516) + else { arc << player; } @@ -105,7 +105,7 @@ void DBot::Tick () BotThinkCycles.Clock(); bglobal.m_Thinking = true; - bglobal.Think (player->mo, &netcmds[player - players][((gametic + 1)/ticdup)%BACKUPTICS]); + Think (); bglobal.m_Thinking = false; BotThinkCycles.Unclock(); } diff --git a/src/b_bot.h b/src/b_bot.h index 7740e5e00..69a2f7774 100644 --- a/src/b_bot.h +++ b/src/b_bot.h @@ -89,7 +89,7 @@ public: void ClearPlayer (int playernum, bool keepTeam); - //(B_Game.c) + //(b_game.cpp) void Main (); void Init (); void End(); @@ -99,23 +99,16 @@ public: bool LoadBots (); void ForgetBots (); - //(B_Func.c) - bool Check_LOS (AActor *mobj1, AActor *mobj2, angle_t vangle); + //(b_func.cpp) void StartTravel (); void FinishTravel (); + bool IsLeader (player_t *player); + void SetBodyAt (fixed_t x, fixed_t y, fixed_t z, int hostnum); + fixed_t FakeFire (AActor *source, AActor *dest, ticcmd_t *cmd); + bool SafeCheckPosition (AActor *actor, fixed_t x, fixed_t y, FCheckPosition &tm); - //(B_Think.c) - void Think (AActor *actor, ticcmd_t *cmd); - void WhatToGet (AActor *actor, AActor *item); - - //(B_move.c) - void Roam (AActor *actor, ticcmd_t *cmd); - bool Move (AActor *actor, ticcmd_t *cmd); - bool TryWalk (AActor *actor, ticcmd_t *cmd); - void NewChaseDir (AActor *actor, ticcmd_t *cmd); + //(b_move.cpp) bool CleanAhead (AActor *thing, fixed_t x, fixed_t y, ticcmd_t *cmd); - void TurnToAng (AActor *actor); - void Pitch (AActor *actor, AActor *target); bool IsDangerous (sector_t *sec); TArray getspawned; //Array of bots (their names) which should be spawned when starting a game. @@ -132,24 +125,9 @@ public: bool m_Thinking; private: - //(B_Game.c) + //(b_game.cpp) bool DoAddBot (BYTE *info, botskill_t skill); - //(B_Func.c) - bool Reachable (AActor *actor, AActor *target); - void Dofire (AActor *actor, ticcmd_t *cmd); - bool IsLeader (player_t *player); - AActor *Choose_Mate (AActor *bot); - AActor *Find_enemy (AActor *bot); - void SetBodyAt (fixed_t x, fixed_t y, fixed_t z, int hostnum); - fixed_t FakeFire (AActor *source, AActor *dest, ticcmd_t *cmd); - angle_t FireRox (AActor *bot, AActor *enemy, ticcmd_t *cmd); - bool SafeCheckPosition (AActor *actor, fixed_t x, fixed_t y, FCheckPosition &tm); - - //(B_Think.c) - void ThinkForMove (AActor *actor, ticcmd_t *cmd); - void Set_enemy (AActor *actor); - protected: bool ctf; int loaded_bots; @@ -168,13 +146,17 @@ public: void Serialize (FArchive &arc); void Tick (); + //(b_think.cpp) + void WhatToGet (AActor *item); + + //(b_func.cpp) + bool Check_LOS (AActor *to, angle_t vangle); + player_t *player; angle_t angle; // The wanted angle that the bot try to get every tic. // (used to get a smooth view movement) TObjPtr dest; // Move Destination. TObjPtr prev; // Previous move destination. - - TObjPtr enemy; // The dead meat. TObjPtr missile; // A threatening missile that needs to be avoided. TObjPtr mate; // Friend (used for grouping in teamplay or coop). @@ -204,6 +186,27 @@ public: fixed_t oldx; fixed_t oldy; + +private: + //(B_think.cpp) + void Think (); + void ThinkForMove (ticcmd_t *cmd); + void Set_enemy (); + + //(B_func.cpp) + bool Reachable (AActor *target); + void Dofire (ticcmd_t *cmd); + AActor *Choose_Mate (); + AActor *Find_enemy (); + angle_t FireRox (AActor *enemy, ticcmd_t *cmd); + + //(b_move.cpp) + void Roam (ticcmd_t *cmd); + bool Move (ticcmd_t *cmd); + bool TryWalk (ticcmd_t *cmd); + void NewChaseDir (ticcmd_t *cmd); + void TurnToAng (); + void Pitch (AActor *target); }; @@ -220,7 +223,3 @@ EXTERN_CVAR (Bool, bot_watersplash) EXTERN_CVAR (Bool, bot_chat) #endif // __B_BOT_H__ - - - - diff --git a/src/b_func.cpp b/src/b_func.cpp index 349155a81..7165d2cc1 100644 --- a/src/b_func.cpp +++ b/src/b_func.cpp @@ -24,24 +24,23 @@ static FRandom pr_botdofire ("BotDoFire"); -//Checks TRUE reachability from -//one looker to another. First mobj (looker) is looker. -bool FCajunMaster::Reachable (AActor *looker, AActor *rtarget) +//Checks TRUE reachability from bot to a looker. +bool DBot::Reachable (AActor *rtarget) { - if (looker == rtarget) + if (player->mo == rtarget) return false; if ((rtarget->Sector->ceilingplane.ZatPoint (rtarget->x, rtarget->y) - rtarget->Sector->floorplane.ZatPoint (rtarget->x, rtarget->y)) - < looker->height) //Where rtarget is, looker can't be. + < player->mo->height) //Where rtarget is, player->mo can't be. return false; - sector_t *last_s = looker->Sector; - fixed_t last_z = last_s->floorplane.ZatPoint (looker->x, looker->y); - fixed_t estimated_dist = P_AproxDistance (looker->x - rtarget->x, looker->y - rtarget->y); + sector_t *last_s = player->mo->Sector; + fixed_t last_z = last_s->floorplane.ZatPoint (player->mo->x, player->mo->y); + fixed_t estimated_dist = P_AproxDistance (player->mo->x - rtarget->x, player->mo->y - rtarget->y); bool reachable = true; - FPathTraverse it(looker->x+looker->velx, looker->y+looker->vely, rtarget->x, rtarget->y, PT_ADDLINES|PT_ADDTHINGS); + FPathTraverse it(player->mo->x+player->mo->velx, player->mo->y+player->mo->vely, rtarget->x, rtarget->y, PT_ADDLINES|PT_ADDTHINGS); intercept_t *in; while ((in = it.Next())) { @@ -55,8 +54,8 @@ bool FCajunMaster::Reachable (AActor *looker, AActor *rtarget) frac = in->frac - FixedDiv (4*FRACUNIT, MAX_TRAVERSE_DIST); dist = FixedMul (frac, MAX_TRAVERSE_DIST); - hitx = it.Trace().x + FixedMul (looker->velx, frac); - hity = it.Trace().y + FixedMul (looker->vely, frac); + hitx = it.Trace().x + FixedMul (player->mo->velx, frac); + hity = it.Trace().y + FixedMul (player->mo->vely, frac); if (in->isaline) { @@ -76,7 +75,7 @@ bool FCajunMaster::Reachable (AActor *looker, AActor *rtarget) if (!bglobal.IsDangerous (s) && //Any nukage/lava? (floorheight <= (last_z+MAXMOVEHEIGHT) && ((ceilingheight == floorheight && line->special) - || (ceilingheight - floorheight) >= looker->height))) //Does it fit? + || (ceilingheight - floorheight) >= player->mo->height))) //Does it fit? { last_z = floorheight; last_s = s; @@ -95,7 +94,7 @@ bool FCajunMaster::Reachable (AActor *looker, AActor *rtarget) } thing = in->d.thing; - if (thing == looker) //Can't reach self in this case. + if (thing == player->mo) //Can't reach self in this case. continue; if (thing == rtarget && (rtarget->Sector->floorplane.ZatPoint (rtarget->x, rtarget->y) <= (last_z+MAXMOVEHEIGHT))) { @@ -115,16 +114,16 @@ bool FCajunMaster::Reachable (AActor *looker, AActor *rtarget) //if these conditions are true, the function returns true. //GOOD TO KNOW is that the player's view angle //in doom is 90 degrees infront. -bool FCajunMaster::Check_LOS (AActor *from, AActor *to, angle_t vangle) +bool DBot::Check_LOS (AActor *to, angle_t vangle) { - if (!P_CheckSight (from, to, SF_SEEPASTBLOCKEVERYTHING)) + if (!P_CheckSight (player->mo, to, SF_SEEPASTBLOCKEVERYTHING)) return false; // out of sight if (vangle == ANGLE_MAX) return true; if (vangle == 0) return false; //Looker seems to be blind. - return (angle_t)abs (R_PointToAngle2 (from->x, from->y, to->x, to->y) - from->angle) <= vangle/2; + return (angle_t)abs (R_PointToAngle2 (player->mo->x, player->mo->y, to->x, to->y) - player->mo->angle) <= vangle/2; } //------------------------------------- @@ -132,7 +131,7 @@ bool FCajunMaster::Check_LOS (AActor *from, AActor *to, angle_t vangle) //------------------------------------- //The bot will check if it's time to fire //and do so if that is the case. -void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd) +void DBot::Dofire (ticcmd_t *cmd) { bool no_fire; //used to prevent bot from pumping rockets into nearby walls. int aiming_penalty=0; //For shooting at shading target, if screen is red, MAKEME: When screen red. @@ -140,49 +139,48 @@ void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd) fixed_t dist; angle_t an; int m; - AActor *enemy = actor->player->Bot->enemy; if (!enemy || !(enemy->flags & MF_SHOOTABLE) || enemy->health <= 0) return; - if (actor->player->ReadyWeapon == NULL) + if (player->ReadyWeapon == NULL) return; - if (actor->player->damagecount > actor->player->Bot->skill.isp) + if (player->damagecount > skill.isp) { - actor->player->Bot->first_shot = true; + first_shot = true; return; } //Reaction skill thing. - if (actor->player->Bot->first_shot && - !(actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_REACTION_SKILL_THING)) + if (first_shot && + !(player->ReadyWeapon->WeaponFlags & WIF_BOT_REACTION_SKILL_THING)) { - actor->player->Bot->t_react = (100-actor->player->Bot->skill.reaction+1)/((pr_botdofire()%3)+3); + t_react = (100-skill.reaction+1)/((pr_botdofire()%3)+3); } - actor->player->Bot->first_shot = false; - if (actor->player->Bot->t_react) + first_shot = false; + if (t_react) return; //MAKEME: Decrease the rocket suicides even more. no_fire = true; - //actor->player->angle = R_PointToAngle2(actor->x, actor->y, actor->player->enemy->x, actor->player->enemy->y); + //angle = R_PointToAngle2(player->mo->x, player->mo->y, player->enemy->x, player->enemy->y); //Distance to enemy. - dist = P_AproxDistance ((actor->x + actor->velx) - (enemy->x + enemy->velx), - (actor->y + actor->vely) - (enemy->y + enemy->vely)); + dist = P_AproxDistance ((player->mo->x + player->mo->velx) - (enemy->x + enemy->velx), + (player->mo->y + player->mo->vely) - (enemy->y + enemy->vely)); //FIRE EACH TYPE OF WEAPON DIFFERENT: Here should all the different weapons go. - if (actor->player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON) + if (player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON) { - if ((actor->player->ReadyWeapon->ProjectileType != NULL)) + if ((player->ReadyWeapon->ProjectileType != NULL)) { - if (actor->player->ReadyWeapon->CheckAmmo (AWeapon::PrimaryFire, false, true)) + if (player->ReadyWeapon->CheckAmmo (AWeapon::PrimaryFire, false, true)) { // This weapon can fire a projectile and has enough ammo to do so goto shootmissile; } - else if (!(actor->player->ReadyWeapon->WeaponFlags & WIF_AMMO_OPTIONAL)) + else if (!(player->ReadyWeapon->WeaponFlags & WIF_AMMO_OPTIONAL)) { // Ammo is required, so don't shoot. This is for weapons that shoot // missiles that die at close range, such as the powered-up Phoneix Rod. @@ -195,51 +193,51 @@ void FCajunMaster::Dofire (AActor *actor, ticcmd_t *cmd) no_fire = (dist > (MELEERANGE*4)); } } - else if (actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_BFG) + else if (player->ReadyWeapon->WeaponFlags & WIF_BOT_BFG) { //MAKEME: This should be smarter. - if ((pr_botdofire()%200)<=actor->player->Bot->skill.reaction) - if(Check_LOS(actor, actor->player->Bot->enemy, SHOOTFOV)) + if ((pr_botdofire()%200)<=skill.reaction) + if(Check_LOS(enemy, SHOOTFOV)) no_fire = false; } - else if (actor->player->ReadyWeapon->ProjectileType != NULL) + else if (player->ReadyWeapon->ProjectileType != NULL) { - if (actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) + if (player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) { //Special rules for RL - an = FireRox (actor, enemy, cmd); + an = FireRox (enemy, cmd); if(an) { - actor->player->Bot->angle = an; + angle = an; //have to be somewhat precise. to avoid suicide. - if (abs (actor->player->Bot->angle - actor->angle) < 12*ANGLE_1) + if (abs (angle - player->mo->angle) < 12*ANGLE_1) { - actor->player->Bot->t_rocket = 9; + t_rocket = 9; no_fire = false; } } } // prediction aiming shootmissile: - dist = P_AproxDistance (actor->x - enemy->x, actor->y - enemy->y); - m = dist / GetDefaultByType (actor->player->ReadyWeapon->ProjectileType)->Speed; - SetBodyAt (enemy->x + enemy->velx*m*2, enemy->y + enemy->vely*m*2, enemy->z, 1); - actor->player->Bot->angle = R_PointToAngle2 (actor->x, actor->y, body1->x, body1->y); - if (Check_LOS (actor, enemy, SHOOTFOV)) + dist = P_AproxDistance (player->mo->x - enemy->x, player->mo->y - enemy->y); + m = dist / GetDefaultByType (player->ReadyWeapon->ProjectileType)->Speed; + bglobal.SetBodyAt (enemy->x + enemy->velx*m*2, enemy->y + enemy->vely*m*2, enemy->z, 1); + angle = R_PointToAngle2 (player->mo->x, player->mo->y, bglobal.body1->x, bglobal.body1->y); + if (Check_LOS (enemy, SHOOTFOV)) no_fire = false; } else { //Other weapons, mostly instant hit stuff. - actor->player->Bot->angle = R_PointToAngle2 (actor->x, actor->y, enemy->x, enemy->y); + angle = R_PointToAngle2 (player->mo->x, player->mo->y, enemy->x, enemy->y); aiming_penalty = 0; if (enemy->flags & MF_SHADOW) aiming_penalty += (pr_botdofire()%25)+10; - if (enemy->Sector->lightlevelplayer->powers & PW_INFRARED)*/) + if (enemy->Sector->lightlevelpowers & PW_INFRARED)*/) aiming_penalty += pr_botdofire()%40;//Dark - if (actor->player->damagecount) - aiming_penalty += actor->player->damagecount; //Blood in face makes it hard to aim - aiming_value = actor->player->Bot->skill.aiming - aiming_penalty; + if (player->damagecount) + aiming_penalty += player->damagecount; //Blood in face makes it hard to aim + aiming_value = skill.aiming - aiming_penalty; if (aiming_value <= 0) aiming_value = 1; m = ((SHOOTFOV/2)-(aiming_value*SHOOTFOV/200)); //Higher skill is more accurate @@ -248,18 +246,18 @@ shootmissile: if (m) { - if (actor->player->Bot->increase) - actor->player->Bot->angle += m; + if (increase) + angle += m; else - actor->player->Bot->angle -= m; + angle -= m; } - if (abs (actor->player->Bot->angle - actor->angle) < 4*ANGLE_1) + if (abs (angle - player->mo->angle) < 4*ANGLE_1) { - actor->player->Bot->increase = !actor->player->Bot->increase; + increase = !increase; } - if (Check_LOS (actor, enemy, (SHOOTFOV/2))) + if (Check_LOS (enemy, (SHOOTFOV/2))) no_fire = false; } if (!no_fire) //If going to fire weapon @@ -267,7 +265,7 @@ shootmissile: cmd->ucmd.buttons |= BT_ATTACK; } //Prevents bot from jerking, when firing automatic things with low skill. - //actor->angle = R_PointToAngle2(actor->x, actor->y, actor->player->enemy->x, actor->player->enemy->y); + //player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, player->enemy->x, player->enemy->y); } bool FCajunMaster::IsLeader (player_t *player) @@ -287,7 +285,7 @@ bool FCajunMaster::IsLeader (player_t *player) //This function is called every //tick (for each bot) to set //the mate (teammate coop mate). -AActor *FCajunMaster::Choose_Mate (AActor *bot) +AActor *DBot::Choose_Mate () { int count; fixed_t closest_dist, test; @@ -295,20 +293,20 @@ AActor *FCajunMaster::Choose_Mate (AActor *bot) AActor *observer; //is mate alive? - if (bot->player->Bot->mate) + if (mate) { - if (bot->player->Bot->mate->health <= 0) - bot->player->Bot->mate = NULL; + if (mate->health <= 0) + mate = NULL; else - bot->player->Bot->last_mate = bot->player->Bot->mate; + last_mate = mate; } - if (bot->player->Bot->mate) //Still is.. - return bot->player->Bot->mate; + if (mate) //Still is.. + return mate; //Check old_mates status. - if (bot->player->Bot->last_mate) - if (bot->player->Bot->last_mate->health <= 0) - bot->player->Bot->last_mate = NULL; + if (last_mate) + if (last_mate->health <= 0) + last_mate = NULL; target = NULL; closest_dist = FIXED_MAX; @@ -324,17 +322,17 @@ AActor *FCajunMaster::Choose_Mate (AActor *bot) if (playeringame[count] && client->mo - && bot != client->mo - && (bot->IsTeammate (client->mo) || !deathmatch) + && player->mo != client->mo + && (player->mo->IsTeammate (client->mo) || !deathmatch) && client->mo->health > 0 && client->mo != observer - && ((bot->health/2) <= client->mo->health || !deathmatch) - && !IsLeader(client)) //taken? + && ((player->mo->health/2) <= client->mo->health || !deathmatch) + && !bglobal.IsLeader(client)) //taken? { - if (P_CheckSight (bot, client->mo, SF_IGNOREVISIBILITY)) + if (P_CheckSight (player->mo, client->mo, SF_IGNOREVISIBILITY)) { - test = P_AproxDistance (client->mo->x - bot->x, - client->mo->y - bot->y); + test = P_AproxDistance (client->mo->x - player->mo->x, + client->mo->y - player->mo->y); if (test < closest_dist) { @@ -347,15 +345,15 @@ AActor *FCajunMaster::Choose_Mate (AActor *bot) /* //Make a introducing to mate. - if(target && target!=bot->player->last_mate) + if(target && target!=last_mate) { if((P_Random()%(200*bglobal.botnum))<3) { - bot->player->chat = c_teamup; + chat = c_teamup; if(target->bot) - strcpy(bot->player->c_target, botsingame[target->bot_id]); + strcpy(c_target, botsingame[target->bot_id]); else if(target->player) - strcpy(bot->player->c_target, player_names[target->play_id]); + strcpy(c_target, player_names[target->play_id]); } } */ @@ -365,7 +363,7 @@ AActor *FCajunMaster::Choose_Mate (AActor *bot) } //MAKEME: Make this a smart decision -AActor *FCajunMaster::Find_enemy (AActor *bot) +AActor *DBot::Find_enemy () { int count; fixed_t closest_dist, temp; //To target. @@ -375,15 +373,15 @@ AActor *FCajunMaster::Find_enemy (AActor *bot) if (!deathmatch) { // [RH] Take advantage of the Heretic/Hexen code to be a little smarter - return P_RoughMonsterSearch (bot, 20); + return P_RoughMonsterSearch (player->mo, 20); } //Note: It's hard to ambush a bot who is not alone - if (bot->player->Bot->allround || bot->player->Bot->mate) + if (allround || mate) vangle = ANGLE_MAX; else vangle = ENEMY_SCAN_FOV; - bot->player->Bot->allround = false; + allround = false; target = NULL; closest_dist = FIXED_MAX; @@ -396,21 +394,21 @@ AActor *FCajunMaster::Find_enemy (AActor *bot) { player_t *client = &players[count]; if (playeringame[count] - && !bot->IsTeammate (client->mo) + && !player->mo->IsTeammate (client->mo) && client->mo != observer && client->mo->health > 0 - && bot != client->mo) + && player->mo != client->mo) { - if (Check_LOS (bot, client->mo, vangle)) //Here's a strange one, when bot is standing still, the P_CheckSight within Check_LOS almost always returns false. tought it should be the same checksight as below but.. (below works) something must be fuckin wierd screded up. - //if(P_CheckSight(bot, players[count].mo)) + if (Check_LOS (client->mo, vangle)) //Here's a strange one, when bot is standing still, the P_CheckSight within Check_LOS almost always returns false. tought it should be the same checksight as below but.. (below works) something must be fuckin wierd screded up. + //if(P_CheckSight(player->mo, players[count].mo)) { - temp = P_AproxDistance (client->mo->x - bot->x, - client->mo->y - bot->y); + temp = P_AproxDistance (client->mo->x - player->mo->x, + client->mo->y - player->mo->y); //Too dark? if (temp > DARK_DIST && client->mo->Sector->lightlevel < WHATS_DARK /*&& - bot->player->Powers & PW_INFRARED*/) + player->Powers & PW_INFRARED*/) continue; if (temp < closest_dist) @@ -494,16 +492,16 @@ fixed_t FCajunMaster::FakeFire (AActor *source, AActor *dest, ticcmd_t *cmd) return dist; } -angle_t FCajunMaster::FireRox (AActor *bot, AActor *enemy, ticcmd_t *cmd) +angle_t DBot::FireRox (AActor *enemy, ticcmd_t *cmd) { fixed_t dist; angle_t ang; AActor *actor; int m; - SetBodyAt (bot->x + FixedMul(bot->velx, 5*FRACUNIT), - bot->y + FixedMul(bot->vely, 5*FRACUNIT), - bot->z + (bot->height / 2), 2); + bglobal.SetBodyAt (player->mo->x + FixedMul(player->mo->velx, 5*FRACUNIT), + player->mo->y + FixedMul(player->mo->vely, 5*FRACUNIT), + player->mo->z + (player->mo->height / 2), 2); actor = bglobal.body2; @@ -513,16 +511,16 @@ angle_t FCajunMaster::FireRox (AActor *bot, AActor *enemy, ticcmd_t *cmd) //Predict. m = (((dist+1)/FRACUNIT) / GetDefaultByName("Rocket")->Speed); - SetBodyAt (enemy->x + FixedMul(enemy->velx, (m+2*FRACUNIT)), - enemy->y + FixedMul(enemy->vely, (m+2*FRACUNIT)), ONFLOORZ, 1); + bglobal.SetBodyAt (enemy->x + FixedMul(enemy->velx, (m+2*FRACUNIT)), + enemy->y + FixedMul(enemy->vely, (m+2*FRACUNIT)), ONFLOORZ, 1); dist = P_AproxDistance(actor->x-bglobal.body1->x, actor->y-bglobal.body1->y); //try the predicted location if (P_CheckSight (actor, bglobal.body1, SF_IGNOREVISIBILITY)) //See the predicted location, so give a test missile { FCheckPosition tm; - if (SafeCheckPosition (bot, actor->x, actor->y, tm)) + if (bglobal.SafeCheckPosition (player->mo, actor->x, actor->y, tm)) { - if (FakeFire (actor, bglobal.body1, cmd) >= SAFE_SELF_MISDIST) + if (bglobal.FakeFire (actor, bglobal.body1, cmd) >= SAFE_SELF_MISDIST) { ang = R_PointToAngle2 (actor->x, actor->y, bglobal.body1->x, bglobal.body1->y); return ang; @@ -532,9 +530,9 @@ angle_t FCajunMaster::FireRox (AActor *bot, AActor *enemy, ticcmd_t *cmd) //Try fire straight. if (P_CheckSight (actor, enemy, 0)) { - if (FakeFire (bot, enemy, cmd) >= SAFE_SELF_MISDIST) + if (bglobal.FakeFire (player->mo, enemy, cmd) >= SAFE_SELF_MISDIST) { - ang = R_PointToAngle2(bot->x, bot->y, enemy->x, enemy->y); + ang = R_PointToAngle2(player->mo->x, player->mo->y, enemy->x, enemy->y); return ang; } } diff --git a/src/b_move.cpp b/src/b_move.cpp index f020cf4fc..fd0405457 100644 --- a/src/b_move.cpp +++ b/src/b_move.cpp @@ -27,57 +27,57 @@ static FRandom pr_botnewchasedir ("BotNewChaseDir"); extern dirtype_t opposite[9]; extern dirtype_t diags[4]; -//Called while the bot moves after its player->dest mobj +//Called while the bot moves after its dest mobj //which can be a weapon/enemy/item whatever. -void FCajunMaster::Roam (AActor *actor, ticcmd_t *cmd) +void DBot::Roam (ticcmd_t *cmd) { int delta; - if (Reachable(actor, actor->player->Bot->dest)) + if (Reachable(dest)) { // Straight towards it. - actor->player->Bot->angle = R_PointToAngle2(actor->x, actor->y, actor->player->Bot->dest->x, actor->player->Bot->dest->y); + angle = R_PointToAngle2(player->mo->x, player->mo->y, dest->x, dest->y); } - else if (actor->movedir < 8) // turn towards movement direction if not there yet + else if (player->mo->movedir < 8) // turn towards movement direction if not there yet { - actor->player->Bot->angle &= (angle_t)(7<<29); - delta = actor->player->Bot->angle - (actor->movedir << 29); + angle &= (angle_t)(7<<29); + delta = angle - (player->mo->movedir << 29); if (delta > 0) - actor->player->Bot->angle -= ANG45; + angle -= ANG45; else if (delta < 0) - actor->player->Bot->angle += ANG45; + angle += ANG45; } // chase towards destination. - if (--actor->movecount < 0 || !Move (actor, cmd)) + if (--player->mo->movecount < 0 || !Move (cmd)) { - NewChaseDir (actor, cmd); + NewChaseDir (cmd); } } -bool FCajunMaster::Move (AActor *actor, ticcmd_t *cmd) +bool DBot::Move (ticcmd_t *cmd) { fixed_t tryx, tryy; bool try_ok; int good; - if (actor->movedir == DI_NODIR) + if (player->mo->movedir == DI_NODIR) return false; - if ((unsigned)actor->movedir >= 8) + if ((unsigned)player->mo->movedir >= 8) I_Error ("Weird bot movedir!"); - tryx = actor->x + 8*xspeed[actor->movedir]; - tryy = actor->y + 8*yspeed[actor->movedir]; + tryx = player->mo->x + 8*xspeed[player->mo->movedir]; + tryy = player->mo->y + 8*yspeed[player->mo->movedir]; - try_ok = CleanAhead (actor, tryx, tryy, cmd); + try_ok = bglobal.CleanAhead (player->mo, tryx, tryy, cmd); if (!try_ok) //Anything blocking that could be opened etc.. { if (!spechit.Size ()) return false; - actor->movedir = DI_NODIR; + player->mo->movedir = DI_NODIR; good = 0; line_t *ld; @@ -86,16 +86,16 @@ bool FCajunMaster::Move (AActor *actor, ticcmd_t *cmd) { bool tryit = true; - if (ld->special == Door_LockedRaise && !P_CheckKeys (actor, ld->args[3], false)) + if (ld->special == Door_LockedRaise && !P_CheckKeys (player->mo, ld->args[3], false)) tryit = false; - else if (ld->special == Generic_Door && !P_CheckKeys (actor, ld->args[4], false)) + else if (ld->special == Generic_Door && !P_CheckKeys (player->mo, ld->args[4], false)) tryit = false; if (tryit && - (P_TestActivateLine (ld, actor, 0, SPAC_Use) || - P_TestActivateLine (ld, actor, 0, SPAC_Push))) + (P_TestActivateLine (ld, player->mo, 0, SPAC_Use) || + P_TestActivateLine (ld, player->mo, 0, SPAC_Push))) { - good |= ld == actor->BlockingLine ? 1 : 2; + good |= ld == player->mo->BlockingLine ? 1 : 2; } } if (good && ((pr_botopendoor() >= 203) ^ (good & 1))) @@ -113,16 +113,16 @@ bool FCajunMaster::Move (AActor *actor, ticcmd_t *cmd) return true; } -bool FCajunMaster::TryWalk (AActor *actor, ticcmd_t *cmd) +bool DBot::TryWalk (ticcmd_t *cmd) { - if (!Move (actor, cmd)) + if (!Move (cmd)) return false; - actor->movecount = pr_bottrywalk() & 60; + player->mo->movecount = pr_bottrywalk() & 60; return true; } -void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd) +void DBot::NewChaseDir (ticcmd_t *cmd) { fixed_t deltax; fixed_t deltay; @@ -134,7 +134,7 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd) dirtype_t turnaround; - if (!actor->player->Bot->dest) + if (!dest) { #ifndef BOT_RELEASE_COMPILE Printf ("Bot tried move without destination\n"); @@ -142,11 +142,11 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd) return; } - olddir = (dirtype_t)actor->movedir; + olddir = (dirtype_t)player->mo->movedir; turnaround = opposite[olddir]; - deltax = actor->player->Bot->dest->x - actor->x; - deltay = actor->player->Bot->dest->y - actor->y; + deltax = dest->x - player->mo->x; + deltay = dest->y - player->mo->y; if (deltax > 10*FRACUNIT) d[1] = DI_EAST; @@ -165,8 +165,8 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd) // try direct route if (d[1] != DI_NODIR && d[2] != DI_NODIR) { - actor->movedir = diags[((deltay<0)<<1)+(deltax>0)]; - if (actor->movedir != turnaround && TryWalk(actor, cmd)) + player->mo->movedir = diags[((deltay<0)<<1)+(deltax>0)]; + if (player->mo->movedir != turnaround && TryWalk(cmd)) return; } @@ -186,16 +186,16 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd) if (d[1]!=DI_NODIR) { - actor->movedir = d[1]; - if (TryWalk (actor, cmd)) + player->mo->movedir = d[1]; + if (TryWalk (cmd)) return; } if (d[2]!=DI_NODIR) { - actor->movedir = d[2]; + player->mo->movedir = d[2]; - if (TryWalk(actor, cmd)) + if (TryWalk(cmd)) return; } @@ -203,9 +203,9 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd) // so pick another direction. if (olddir!=DI_NODIR) { - actor->movedir = olddir; + player->mo->movedir = olddir; - if (TryWalk(actor, cmd)) + if (TryWalk(cmd)) return; } @@ -218,9 +218,9 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd) { if (tdir!=turnaround) { - actor->movedir = tdir; + player->mo->movedir = tdir; - if (TryWalk(actor, cmd)) + if (TryWalk(cmd)) return; } } @@ -233,9 +233,9 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd) { if (tdir!=turnaround) { - actor->movedir = tdir; + player->mo->movedir = tdir; - if (TryWalk(actor, cmd)) + if (TryWalk(cmd)) return; } } @@ -243,12 +243,12 @@ void FCajunMaster::NewChaseDir (AActor *actor, ticcmd_t *cmd) if (turnaround != DI_NODIR) { - actor->movedir = turnaround; - if (TryWalk(actor, cmd)) + player->mo->movedir = turnaround; + if (TryWalk(cmd)) return; } - actor->movedir = DI_NODIR; // can not move + player->mo->movedir = DI_NODIR; // can not move } @@ -307,48 +307,48 @@ bool FCajunMaster::CleanAhead (AActor *thing, fixed_t x, fixed_t y, ticcmd_t *cm #define MAXTURN (15*ANGLE_1) //Max degrees turned in one tic. Lower is smother but may cause the bot not getting where it should = crash #define TURNSENS 3 //Higher is smoother but slower turn. -void FCajunMaster::TurnToAng (AActor *actor) +void DBot::TurnToAng () { int maxturn = MAXTURN; - if (actor->player->ReadyWeapon != NULL) + if (player->ReadyWeapon != NULL) { - if (actor->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) + if (player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) { - if (actor->player->Bot->t_roam && !actor->player->Bot->missile) + if (t_roam && !missile) { //Keep angle that where when shot where decided. return; } } - if(actor->player->Bot->enemy) - if(!actor->player->Bot->dest) //happens when running after item in combat situations, or normal, prevents weak turns - if(actor->player->ReadyWeapon->ProjectileType == NULL && !(actor->player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON)) - if(Check_LOS(actor, actor->player->Bot->enemy, SHOOTFOV+5*ANGLE_1)) + if(enemy) + if(!dest) //happens when running after item in combat situations, or normal, prevents weak turns + if(player->ReadyWeapon->ProjectileType == NULL && !(player->ReadyWeapon->WeaponFlags & WIF_MELEEWEAPON)) + if(Check_LOS(enemy, SHOOTFOV+5*ANGLE_1)) maxturn = 3; } - int distance = actor->player->Bot->angle - actor->angle; + int distance = angle - player->mo->angle; - if (abs (distance) < OKAYRANGE && !actor->player->Bot->enemy) + if (abs (distance) < OKAYRANGE && !enemy) return; distance /= TURNSENS; if (abs (distance) > maxturn) distance = distance < 0 ? -maxturn : maxturn; - actor->angle += distance; + player->mo->angle += distance; } -void FCajunMaster::Pitch (AActor *actor, AActor *target) +void DBot::Pitch (AActor *target) { double aim; double diff; - diff = target->z - actor->z; - aim = atan (diff / (double)P_AproxDistance (actor->x - target->x, actor->y - target->y)); - actor->pitch = -(int)(aim * ANGLE_180/M_PI); + diff = target->z - player->mo->z; + aim = atan (diff / (double)P_AproxDistance (player->mo->x - target->x, player->mo->y - target->y)); + player->mo->pitch = -(int)(aim * ANGLE_180/M_PI); } //Checks if a sector is dangerous. @@ -371,4 +371,3 @@ bool FCajunMaster::IsDangerous (sector_t *sec) || special == Damage_InstantDeath || special == sDamage_SuperHellslime; } - diff --git a/src/b_think.cpp b/src/b_think.cpp index e5ee8775c..34baeee9c 100644 --- a/src/b_think.cpp +++ b/src/b_think.cpp @@ -24,47 +24,49 @@ static FRandom pr_botmove ("BotMove"); //This function is called each tic for each bot, //so this is what the bot does. -void FCajunMaster::Think (AActor *actor, ticcmd_t *cmd) +void DBot::Think () { + ticcmd_t *cmd = &netcmds[player - players][((gametic + 1)/ticdup)%BACKUPTICS]; + memset (cmd, 0, sizeof(*cmd)); - if (actor->player->Bot->enemy && actor->player->Bot->enemy->health <= 0) - actor->player->Bot->enemy = NULL; + if (enemy && enemy->health <= 0) + enemy = NULL; - if (actor->health > 0) //Still alive + if (player->mo->health > 0) //Still alive { if (teamplay || !deathmatch) - actor->player->Bot->mate = Choose_Mate (actor); + mate = Choose_Mate (); - angle_t oldyaw = actor->angle; - int oldpitch = actor->pitch; + angle_t oldyaw = player->mo->angle; + int oldpitch = player->mo->pitch; - Set_enemy (actor); - ThinkForMove (actor, cmd); - TurnToAng (actor); + Set_enemy (); + ThinkForMove (cmd); + TurnToAng (); - cmd->ucmd.yaw = (short)((actor->angle - oldyaw) >> 16) / ticdup; - cmd->ucmd.pitch = (short)((oldpitch - actor->pitch) >> 16); + cmd->ucmd.yaw = (short)((player->mo->angle - oldyaw) >> 16) / ticdup; + cmd->ucmd.pitch = (short)((oldpitch - player->mo->pitch) >> 16); if (cmd->ucmd.pitch == -32768) cmd->ucmd.pitch = -32767; cmd->ucmd.pitch /= ticdup; - actor->angle = oldyaw + (cmd->ucmd.yaw << 16) * ticdup; - actor->pitch = oldpitch - (cmd->ucmd.pitch << 16) * ticdup; + player->mo->angle = oldyaw + (cmd->ucmd.yaw << 16) * ticdup; + player->mo->pitch = oldpitch - (cmd->ucmd.pitch << 16) * ticdup; } - if (actor->player->Bot->t_active) actor->player->Bot->t_active--; - if (actor->player->Bot->t_strafe) actor->player->Bot->t_strafe--; - if (actor->player->Bot->t_react) actor->player->Bot->t_react--; - if (actor->player->Bot->t_fight) actor->player->Bot->t_fight--; - if (actor->player->Bot->t_rocket) actor->player->Bot->t_rocket--; - if (actor->player->Bot->t_roam) actor->player->Bot->t_roam--; + if (t_active) t_active--; + if (t_strafe) t_strafe--; + if (t_react) t_react--; + if (t_fight) t_fight--; + if (t_rocket) t_rocket--; + if (t_roam) t_roam--; //Respawn ticker - if (actor->player->Bot->t_respawn) + if (t_respawn) { - actor->player->Bot->t_respawn--; + t_respawn--; } - else if (actor->health <= 0) + else if (player->mo->health <= 0) { // Time to respawn cmd->ucmd.buttons |= BT_USE; } @@ -72,62 +74,57 @@ void FCajunMaster::Think (AActor *actor, ticcmd_t *cmd) //how the bot moves. //MAIN movement function. -void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd) +void DBot::ThinkForMove (ticcmd_t *cmd) { - player_t *b; fixed_t dist; bool stuck; int r; - b = actor->player; - if (b->Bot == NULL) - return; - stuck = false; - dist = b->Bot->dest ? P_AproxDistance(actor->x-b->Bot->dest->x, actor->y-b->Bot->dest->y) : 0; + dist = dest ? P_AproxDistance(player->mo->x-dest->x, player->mo->y-dest->y) : 0; - if (b->Bot->missile && - ((!b->Bot->missile->velx || !b->Bot->missile->vely) || !Check_LOS(actor, b->Bot->missile, SHOOTFOV*3/2))) + if (missile && + ((!missile->velx || !missile->vely) || !Check_LOS(missile, SHOOTFOV*3/2))) { - b->Bot->sleft = !b->Bot->sleft; - b->Bot->missile = NULL; //Probably ended its travel. + sleft = !sleft; + missile = NULL; //Probably ended its travel. } - if (actor->pitch > 0) - actor->pitch -= 80; - else if (actor->pitch <= -60) - actor->pitch += 80; + if (player->mo->pitch > 0) + player->mo->pitch -= 80; + else if (player->mo->pitch <= -60) + player->mo->pitch += 80; //HOW TO MOVE: - if (b->Bot->missile && (P_AproxDistance(actor->x-b->Bot->missile->x, actor->y-b->Bot->missile->y)mo->x-missile->x, player->mo->y-missile->y)Bot->missile); - actor->player->Bot->angle = R_PointToAngle2(actor->x, actor->y, b->Bot->missile->x, b->Bot->missile->y); - cmd->ucmd.sidemove = b->Bot->sleft ? -SIDERUN : SIDERUN; + Pitch (missile); + angle = R_PointToAngle2(player->mo->x, player->mo->y, missile->x, missile->y); + cmd->ucmd.sidemove = sleft ? -SIDERUN : SIDERUN; cmd->ucmd.forwardmove = -FORWARDRUN; //Back IS best. - if ((P_AproxDistance(actor->x-b->Bot->oldx, actor->y-b->Bot->oldy)<50000) - && b->Bot->t_strafe<=0) + if ((P_AproxDistance(player->mo->x-oldx, player->mo->y-oldy)<50000) + && t_strafe<=0) { - b->Bot->t_strafe = 5; - b->Bot->sleft = !b->Bot->sleft; + t_strafe = 5; + sleft = !sleft; } //If able to see enemy while avoiding missile, still fire at enemy. - if (b->Bot->enemy && Check_LOS (actor, b->Bot->enemy, SHOOTFOV)) - Dofire (actor, cmd); //Order bot to fire current weapon + if (enemy && Check_LOS (enemy, SHOOTFOV)) + Dofire (cmd); //Order bot to fire current weapon } - else if (b->Bot->enemy && P_CheckSight (actor, b->Bot->enemy, 0)) //Fight! + else if (enemy && P_CheckSight (player->mo, enemy, 0)) //Fight! { - Pitch (actor, b->Bot->enemy); + Pitch (enemy); //Check if it's more important to get an item than fight. - if (b->Bot->dest && (b->Bot->dest->flags&MF_SPECIAL)) //Must be an item, that is close enough. + if (dest && (dest->flags&MF_SPECIAL)) //Must be an item, that is close enough. { -#define is(x) b->Bot->dest->IsKindOf (PClass::FindClass (#x)) +#define is(x) dest->IsKindOf (PClass::FindClass (#x)) if ( ( - (actor->health < b->Bot->skill.isp && + (player->mo->health < skill.isp && (is (Medikit) || is (Stimpack) || is (Soulsphere) || @@ -140,78 +137,78 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd) is (Megasphere) ) || dist < (GETINCOMBAT/4) || - (b->ReadyWeapon == NULL || b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON) + (player->ReadyWeapon == NULL || player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON) ) - && (dist < GETINCOMBAT || (b->ReadyWeapon == NULL || b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) - && Reachable (actor, b->Bot->dest)) + && (dist < GETINCOMBAT || (player->ReadyWeapon == NULL || player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) + && Reachable (dest)) #undef is { goto roam; //Pick it up, no matter the situation. All bonuses are nice close up. } } - b->Bot->dest = NULL; //To let bot turn right + dest = NULL; //To let bot turn right - if (b->ReadyWeapon != NULL && !(b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) - actor->flags &= ~MF_DROPOFF; //Don't jump off any ledges when fighting. + if (player->ReadyWeapon != NULL && !(player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) + player->mo->flags &= ~MF_DROPOFF; //Don't jump off any ledges when fighting. - if (!(b->Bot->enemy->flags3 & MF3_ISMONSTER)) - b->Bot->t_fight = AFTERTICS; + if (!(enemy->flags3 & MF3_ISMONSTER)) + t_fight = AFTERTICS; - if (b->Bot->t_strafe <= 0 && - (P_AproxDistance(actor->x-b->Bot->oldx, actor->y-b->Bot->oldy)<50000 + if (t_strafe <= 0 && + (P_AproxDistance(player->mo->x-oldx, player->mo->y-oldy)<50000 || ((pr_botmove()%30)==10)) ) { stuck = true; - b->Bot->t_strafe = 5; - b->Bot->sleft = !b->Bot->sleft; + t_strafe = 5; + sleft = !sleft; } - b->Bot->angle = R_PointToAngle2(actor->x, actor->y, b->Bot->enemy->x, b->Bot->enemy->y); + angle = R_PointToAngle2(player->mo->x, player->mo->y, enemy->x, enemy->y); - if (b->ReadyWeapon == NULL || - P_AproxDistance(actor->x-b->Bot->enemy->x, actor->y-b->Bot->enemy->y) > - b->ReadyWeapon->MoveCombatDist) + if (player->ReadyWeapon == NULL || + P_AproxDistance(player->mo->x-enemy->x, player->mo->y-enemy->y) > + player->ReadyWeapon->MoveCombatDist) { // If a monster, use lower speed (just for cooler apperance while strafing down doomed monster) - cmd->ucmd.forwardmove = (b->Bot->enemy->flags3 & MF3_ISMONSTER) ? FORWARDWALK : FORWARDRUN; + cmd->ucmd.forwardmove = (enemy->flags3 & MF3_ISMONSTER) ? FORWARDWALK : FORWARDRUN; } else if (!stuck) //Too close, so move away. { // If a monster, use lower speed (just for cooler apperance while strafing down doomed monster) - cmd->ucmd.forwardmove = (b->Bot->enemy->flags3 & MF3_ISMONSTER) ? -FORWARDWALK : -FORWARDRUN; + cmd->ucmd.forwardmove = (enemy->flags3 & MF3_ISMONSTER) ? -FORWARDWALK : -FORWARDRUN; } //Strafing. - if (b->Bot->enemy->flags3 & MF3_ISMONSTER) //It's just a monster so take it down cool. + if (enemy->flags3 & MF3_ISMONSTER) //It's just a monster so take it down cool. { - cmd->ucmd.sidemove = b->Bot->sleft ? -SIDEWALK : SIDEWALK; + cmd->ucmd.sidemove = sleft ? -SIDEWALK : SIDEWALK; } else { - cmd->ucmd.sidemove = b->Bot->sleft ? -SIDERUN : SIDERUN; + cmd->ucmd.sidemove = sleft ? -SIDERUN : SIDERUN; } - Dofire (actor, cmd); //Order bot to fire current weapon + Dofire (cmd); //Order bot to fire current weapon } - else if (b->Bot->mate && !b->Bot->enemy && (!b->Bot->dest || b->Bot->dest==b->Bot->mate)) //Follow mate move. + else if (mate && !enemy && (!dest || dest==mate)) //Follow mate move. { fixed_t matedist; - Pitch (actor, b->Bot->mate); + Pitch (mate); - if (!Reachable (actor, b->Bot->mate)) + if (!Reachable (mate)) { - if (b->Bot->mate == b->Bot->dest && pr_botmove.Random() < 32) + if (mate == dest && pr_botmove.Random() < 32) { // [RH] If the mate is the dest, pick a new dest sometimes - b->Bot->dest = NULL; + dest = NULL; } goto roam; } - actor->player->Bot->angle = R_PointToAngle2(actor->x, actor->y, b->Bot->mate->x, b->Bot->mate->y); + angle = R_PointToAngle2(player->mo->x, player->mo->y, mate->x, mate->y); - matedist = P_AproxDistance(actor->x - b->Bot->mate->x, actor->y - b->Bot->mate->y); + matedist = P_AproxDistance(player->mo->x - mate->x, player->mo->y - mate->y); if (matedist > (FRIEND_DIST*2)) cmd->ucmd.forwardmove = FORWARDRUN; else if (matedist > FRIEND_DIST) @@ -221,42 +218,42 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd) } else //Roam after something. { - b->Bot->first_shot = true; + first_shot = true; ///// roam: ///// - if (b->Bot->enemy && Check_LOS (actor, b->Bot->enemy, SHOOTFOV*3/2)) //If able to see enemy while avoiding missile , still fire at it. - Dofire (actor, cmd); //Order bot to fire current weapon + if (enemy && Check_LOS (enemy, SHOOTFOV*3/2)) //If able to see enemy while avoiding missile , still fire at it. + Dofire (cmd); //Order bot to fire current weapon - if (b->Bot->dest && !(b->Bot->dest->flags&MF_SPECIAL) && b->Bot->dest->health < 0) + if (dest && !(dest->flags&MF_SPECIAL) && dest->health < 0) { //Roaming after something dead. - b->Bot->dest = NULL; + dest = NULL; } - if (b->Bot->dest == NULL) + if (dest == NULL) { - if (b->Bot->t_fight && b->Bot->enemy) //Enemy/bot has jumped around corner. So what to do? + if (t_fight && enemy) //Enemy/bot has jumped around corner. So what to do? { - if (b->Bot->enemy->player) + if (enemy->player) { - if (((b->Bot->enemy->player->ReadyWeapon != NULL && b->Bot->enemy->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) || - (pr_botmove()%100)>b->Bot->skill.isp) && b->ReadyWeapon != NULL && !(b->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) - b->Bot->dest = b->Bot->enemy;//Dont let enemy kill the bot by supressive fire. So charge enemy. - else //hide while b->t_fight, but keep view at enemy. - b->Bot->angle = R_PointToAngle2(actor->x, actor->y, b->Bot->enemy->x, b->Bot->enemy->y); + if (((enemy->player->ReadyWeapon != NULL && enemy->player->ReadyWeapon->WeaponFlags & WIF_BOT_EXPLOSIVE) || + (pr_botmove()%100)>skill.isp) && player->ReadyWeapon != NULL && !(player->ReadyWeapon->WeaponFlags & WIF_WIMPY_WEAPON)) + dest = enemy;//Dont let enemy kill the bot by supressive fire. So charge enemy. + else //hide while t_fight, but keep view at enemy. + angle = R_PointToAngle2(player->mo->x, player->mo->y, enemy->x, enemy->y); } //Just a monster, so kill it. else - b->Bot->dest = b->Bot->enemy; + dest = enemy; - //VerifFavoritWeapon(actor->player); //Dont know why here.., but it must be here, i know the reason, but not why at this spot, uh. + //VerifFavoritWeapon(player); //Dont know why here.., but it must be here, i know the reason, but not why at this spot, uh. } else //Choose a distant target. to get things going. { r = pr_botmove(); if (r < 128) { - TThinkerIterator it (STAT_INVENTORY, firstthing); + TThinkerIterator it (STAT_INVENTORY, bglobal.firstthing); AInventory *item = it.Next(); if (item != NULL || (item = it.Next()) != NULL) @@ -271,60 +268,53 @@ void FCajunMaster::ThinkForMove (AActor *actor, ticcmd_t *cmd) { item = it.Next(); } - firstthing = item; - b->Bot->dest = item; + bglobal.firstthing = item; + dest = item; } } - else if (b->Bot->mate && (r < 179 || P_CheckSight(actor, b->Bot->mate))) + else if (mate && (r < 179 || P_CheckSight(player->mo, mate))) { - b->Bot->dest = b->Bot->mate; + dest = mate; } else if ((playeringame[(r&(MAXPLAYERS-1))]) && players[(r&(MAXPLAYERS-1))].mo->health > 0) { - b->Bot->dest = players[(r&(MAXPLAYERS-1))].mo; + dest = players[(r&(MAXPLAYERS-1))].mo; } } - if (b->Bot->dest) + if (dest) { - b->Bot->t_roam = MAXROAM; + t_roam = MAXROAM; } } - if (b->Bot->dest) + if (dest) { //Bot has a target so roam after it. - Roam (actor, cmd); + Roam (cmd); } } //End of movement main part. - if (!b->Bot->t_roam && b->Bot->dest) + if (!t_roam && dest) { - b->Bot->prev = b->Bot->dest; - b->Bot->dest = NULL; + prev = dest; + dest = NULL; } - if (b->Bot->t_fight<(AFTERTICS/2)) - actor->flags |= MF_DROPOFF; + if (t_fight<(AFTERTICS/2)) + player->mo->flags |= MF_DROPOFF; - b->Bot->oldx = actor->x; - b->Bot->oldy = actor->y; + oldx = player->mo->x; + oldy = player->mo->y; } //BOT_WhatToGet // //Determines if the bot will roam after an item or not. -void FCajunMaster::WhatToGet (AActor *actor, AActor *item) +void DBot::WhatToGet (AActor *item) { - player_t *b = actor->player; - - if (b == NULL) - { - return; - } - #define typeis(x) item->IsKindOf (PClass::FindClass (#x)) if ((item->renderflags & RF_INVISIBLE) //Under respawn and away. - || item == b->Bot->prev) + || item == prev) { return; } @@ -338,7 +328,7 @@ void FCajunMaster::WhatToGet (AActor *actor, AActor *item) // FIXME AWeapon *heldWeapon; - heldWeapon = static_cast (b->mo->FindInventory (item->GetClass())); + heldWeapon = static_cast (player->mo->FindInventory (item->GetClass())); if (heldWeapon != NULL) { if (!weapgiveammo) @@ -354,39 +344,38 @@ void FCajunMaster::WhatToGet (AActor *actor, AActor *item) { AAmmo *ammo = static_cast (item); const PClass *parent = ammo->GetParentAmmo (); - AInventory *holdingammo = b->mo->FindInventory (parent); + AInventory *holdingammo = player->mo->FindInventory (parent); if (holdingammo != NULL && holdingammo->Amount >= holdingammo->MaxAmount) { return; } } - else if ((typeis (Megasphere) || typeis (Soulsphere) || typeis (HealthBonus)) && actor->health >= deh.MaxSoulsphere) + else if ((typeis (Megasphere) || typeis (Soulsphere) || typeis (HealthBonus)) && player->mo->health >= deh.MaxSoulsphere) return; - else if (item->IsKindOf (RUNTIME_CLASS(AHealth)) && actor->health >= deh.MaxHealth /*MAXHEALTH*/) + else if (item->IsKindOf (RUNTIME_CLASS(AHealth)) && player->mo->health >= deh.MaxHealth /*MAXHEALTH*/) return; - if ((b->Bot->dest == NULL || - !(b->Bot->dest->flags & MF_SPECIAL)/* || - !Reachable (actor, b->dest)*/)/* && - Reachable (actor, item)*/) // Calling Reachable slows this down tremendously + if ((dest == NULL || + !(dest->flags & MF_SPECIAL)/* || + !Reachable (dest)*/)/* && + Reachable (item)*/) // Calling Reachable slows this down tremendously { - b->Bot->prev = b->Bot->dest; - b->Bot->dest = item; - b->Bot->t_roam = MAXROAM; + prev = dest; + dest = item; + t_roam = MAXROAM; } } -void FCajunMaster::Set_enemy (AActor *actor) +void DBot::Set_enemy () { AActor *oldenemy; - AActor **enemy = &actor->player->Bot->enemy; - if (*enemy - && (*enemy)->health > 0 - && P_CheckSight (actor, *enemy)) + if (enemy + && enemy->health > 0 + && P_CheckSight (player->mo, enemy)) { - oldenemy = *enemy; + oldenemy = enemy; } else { @@ -395,15 +384,14 @@ void FCajunMaster::Set_enemy (AActor *actor) // [RH] Don't even bother looking for a different enemy if this is not deathmatch // and we already have an existing enemy. - if (deathmatch || !*enemy) + if (deathmatch || !enemy) { - actor->player->Bot->allround = !!*enemy; - *enemy = NULL; - *enemy = Find_enemy(actor); - if (!*enemy) - *enemy = oldenemy; //Try go for last (it will be NULL if there wasn't anyone) + allround = !!enemy; + enemy = Find_enemy(); + if (!enemy) + enemy = oldenemy; //Try go for last (it will be NULL if there wasn't anyone) } //Verify that that enemy is really something alive that bot can kill. - if (*enemy && (((*enemy)->health < 0 || !((*enemy)->flags&MF_SHOOTABLE)) || actor->IsFriend(*enemy))) - *enemy = NULL; + if (enemy && ((enemy->health < 0 || !(enemy->flags&MF_SHOOTABLE)) || player->mo->IsFriend(enemy))) + enemy = NULL; } diff --git a/src/c_console.cpp b/src/c_console.cpp index c6fe58749..e3e3d98fe 100644 --- a/src/c_console.cpp +++ b/src/c_console.cpp @@ -1554,13 +1554,6 @@ void C_MidPrint (FFont *font, const char *msg) AddToConsole (-1, bar1); AddToConsole (-1, msg); AddToConsole (-1, bar3); - if (Logfile) - { - fputs (logbar, Logfile); - fputs (msg, Logfile); - fputs (logbar, Logfile); - fflush (Logfile); - } StatusBar->AttachMessage (new DHUDMessage (font, msg, 1.5f, 0.375f, 0, 0, (EColorRange)PrintColors[PRINTLEVELS], con_midtime), MAKE_ID('C','N','T','R')); @@ -1578,13 +1571,6 @@ void C_MidPrintBold (FFont *font, const char *msg) AddToConsole (-1, bar2); AddToConsole (-1, msg); AddToConsole (-1, bar3); - if (Logfile) - { - fputs (logbar, Logfile); - fputs (msg, Logfile); - fputs (logbar, Logfile); - fflush (Logfile); - } StatusBar->AttachMessage (new DHUDMessage (font, msg, 1.5f, 0.375f, 0, 0, (EColorRange)PrintColors[PRINTLEVELS+1], con_midtime), MAKE_ID('C','N','T','R')); diff --git a/src/c_cvars.cpp b/src/c_cvars.cpp index ce4e88632..c770bcbcf 100644 --- a/src/c_cvars.cpp +++ b/src/c_cvars.cpp @@ -1515,6 +1515,22 @@ void UnlatchCVars (void) } } +void DestroyCVarsFlagged (DWORD flags) +{ + FBaseCVar *cvar = CVars; + FBaseCVar *next = cvar; + + while(cvar) + { + next = cvar->m_Next; + + if(cvar->Flags & flags) + delete cvar; + + cvar = next; + } +} + void C_SetCVarsToDefaults (void) { FBaseCVar *cvar = CVars; diff --git a/src/c_cvars.h b/src/c_cvars.h index 17d3f7b1d..ed2b16755 100644 --- a/src/c_cvars.h +++ b/src/c_cvars.h @@ -159,6 +159,7 @@ private: friend FBaseCVar *FindCVar (const char *var_name, FBaseCVar **prev); friend FBaseCVar *FindCVarSub (const char *var_name, int namelen); friend void UnlatchCVars (void); + friend void DestroyCVarsFlagged (DWORD flags); friend void C_ArchiveCVars (FConfigFile *f, uint32 filter); friend void C_SetCVarsToDefaults (void); friend void FilterCompactCVars (TArray &cvars, uint32 filter); @@ -190,6 +191,9 @@ FBaseCVar *C_CreateCVar(const char *var_name, ECVarType var_type, DWORD flags); // Called from G_InitNew() void UnlatchCVars (void); +// Destroy CVars with the matching flags; called from CCMD(restart) +void DestroyCVarsFlagged (DWORD flags); + // archive cvars to FILE f void C_ArchiveCVars (FConfigFile *f, uint32 filter); diff --git a/src/cocoa/i_backend_cocoa.mm b/src/cocoa/i_backend_cocoa.mm index bd3dc3115..9085d92db 100644 --- a/src/cocoa/i_backend_cocoa.mm +++ b/src/cocoa/i_backend_cocoa.mm @@ -1619,6 +1619,14 @@ const char* I_GetBackEndName() } +FString OSX_FindApplicationSupport() +{ + NSURL *url = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil]; + if(url == nil) + return FString(); + return [[url path] UTF8String]; +} + // --------------------------------------------------------------------------- diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index d14015ca9..41ad71472 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -2247,7 +2247,10 @@ static int PatchStrings (int dummy) ReplaceSpecialChars (holdstring.LockBuffer()); holdstring.UnlockBuffer(); - GStrings.SetString (Line1, holdstring); + // Account for a discrepancy between Boom's and ZDoom's name for the red skull key pickup message + const char *ll = Line1; + if (!stricmp(ll, "GOTREDSKULL")) ll = "GOTREDSKUL"; + GStrings.SetString (ll, holdstring); DPrintf ("%s set to:\n%s\n", Line1, holdstring.GetChars()); } diff --git a/src/d_iwad.cpp b/src/d_iwad.cpp index 6b430fa03..c03ed7a2f 100644 --- a/src/d_iwad.cpp +++ b/src/d_iwad.cpp @@ -430,27 +430,11 @@ int FIWadManager::IdentifyVersion (TArray &wadfiles, const char *iwad, } } } -#ifdef _WIN32 - FString steam_path = I_GetSteamPath(); - if (steam_path.IsNotEmpty()) + TArray steam_path = I_GetSteamPath(); + for (i = 0; i < steam_path.Size(); ++i) { - static const char *const steam_dirs[] = - { - "doom 2/base", - "final doom/base", - "heretic shadow of the serpent riders/base", - "hexen/base", - "hexen deathkings of the dark citadel/base", - "ultimate doom/base", - "DOOM 3 BFG Edition/base/wads" - }; - steam_path += "/SteamApps/common/"; - for (i = 0; i < countof(steam_dirs); ++i) - { - CheckIWAD (steam_path + steam_dirs[i], &wads[0]); - } + CheckIWAD (steam_path[i], &wads[0]); } -#endif } if (iwadparm != NULL && !wads[0].Path.IsEmpty()) diff --git a/src/d_main.cpp b/src/d_main.cpp index 4fea35301..2ceec3926 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -2578,6 +2578,7 @@ void D_DoomMain (void) new (&gameinfo) gameinfo_t; // Reset gameinfo S_Shutdown(); // free all channels and delete playlist C_ClearAliases(); // CCMDs won't be reinitialized so these need to be deleted here + DestroyCVarsFlagged(CVAR_MOD); // Delete any cvar left by mods GC::FullGC(); // perform one final garbage collection before deleting the class data PClass::ClearRuntimeData(); // clear all runtime generated class data diff --git a/src/g_game.cpp b/src/g_game.cpp index 734500e55..ba53254c2 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -116,6 +116,7 @@ CVAR (Bool, chasedemo, false, 0); CVAR (Bool, storesavepic, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Bool, longsavemessages, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (String, save_dir, "", CVAR_ARCHIVE|CVAR_GLOBALCONFIG); +CVAR (Bool, cl_waitforsave, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); EXTERN_CVAR (Float, con_midtime); //========================================================================== @@ -2147,6 +2148,9 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio filename = G_BuildSaveName ("demosave.zds", -1); } + if (cl_waitforsave) + I_FreezeTime(true); + insave = true; G_SnapshotLevel (); @@ -2156,6 +2160,7 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio { Printf ("Could not create savegame '%s'\n", filename.GetChars()); insave = false; + I_FreezeTime(false); return; } @@ -2232,6 +2237,7 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio } insave = false; + I_FreezeTime(false); } @@ -2636,7 +2642,7 @@ void G_DoPlayDemo (void) { FixPathSeperator (defdemoname); DefaultExtension (defdemoname, ".lmp"); - M_ReadFile (defdemoname, &demobuffer); + M_ReadFileMalloc (defdemoname, &demobuffer); } demo_p = demobuffer; diff --git a/src/g_shared/a_artifacts.cpp b/src/g_shared/a_artifacts.cpp index 4a2d066cd..21006776e 100644 --- a/src/g_shared/a_artifacts.cpp +++ b/src/g_shared/a_artifacts.cpp @@ -60,7 +60,8 @@ bool APowerupGiver::Use (bool pickup) } if (BlendColor != 0) { - power->BlendColor = BlendColor; + if (BlendColor != MakeSpecialColormap(65535)) power->BlendColor = BlendColor; + else power->BlendColor = 0; } if (Mode != NAME_None) { diff --git a/src/g_shared/shared_sbar.cpp b/src/g_shared/shared_sbar.cpp index 2a18801a1..d8e113824 100644 --- a/src/g_shared/shared_sbar.cpp +++ b/src/g_shared/shared_sbar.cpp @@ -1123,7 +1123,7 @@ void DBaseStatusBar::DrawCrosshair () ST_LoadCrosshair(); // Don't draw the crosshair if there is none - if (CrosshairImage == NULL || gamestate == GS_TITLELEVEL) + if (CrosshairImage == NULL || gamestate == GS_TITLELEVEL || camera->health <= 0) { return; } diff --git a/src/m_misc.cpp b/src/m_misc.cpp index c550c4593..7f4fa482d 100644 --- a/src/m_misc.cpp +++ b/src/m_misc.cpp @@ -137,6 +137,32 @@ int M_ReadFile (char const *name, BYTE **buffer) return length; } +// +// M_ReadFile (same as above but use malloc instead of new to allocate the buffer.) +// +int M_ReadFileMalloc (char const *name, BYTE **buffer) +{ + int handle, count, length; + struct stat fileinfo; + BYTE *buf; + + handle = open (name, O_RDONLY | O_BINARY, 0666); + if (handle == -1) + I_Error ("Couldn't read file %s", name); + if (fstat (handle,&fileinfo) == -1) + I_Error ("Couldn't read file %s", name); + length = fileinfo.st_size; + buf = (BYTE*)M_Malloc(length); + count = read (handle, buf, length); + close (handle); + + if (count < length) + I_Error ("Couldn't read file %s", name); + + *buffer = buf; + return length; +} + //--------------------------------------------------------------------------- // // PROC M_FindResponseFile diff --git a/src/m_misc.h b/src/m_misc.h index 90844221f..9599306de 100644 --- a/src/m_misc.h +++ b/src/m_misc.h @@ -33,6 +33,7 @@ extern FGameConfigFile *GameConfig; bool M_WriteFile (char const *name, void *source, int length); int M_ReadFile (char const *name, BYTE **buffer); +int M_ReadFileMalloc (char const *name, BYTE **buffer); void M_FindResponseFile (void); // [RH] M_ScreenShot now accepts a filename parameter. diff --git a/src/namedef.h b/src/namedef.h index 146997309..e0b7e8ba6 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -300,6 +300,7 @@ xx(CallACS) xx(Sqrt) xx(CheckClass) xx(IsPointerEqual) +xx(Pick) // Various actor names which are used internally xx(MapSpot) diff --git a/src/oplsynth/nukedopl3.cpp b/src/oplsynth/nukedopl3.cpp index 90673d855..a520f68e1 100644 --- a/src/oplsynth/nukedopl3.cpp +++ b/src/oplsynth/nukedopl3.cpp @@ -23,11 +23,11 @@ Feedback and Rhythm part calculation information. forums.submarine.org.uk(carbon14, opl3): Tremolo and phase generator calculation information. - OPLx decapsulated(Matthew Gambrell and Olli Niemitalo): + OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): OPL2 ROMs. */ -//version 1.4.2 +//version 1.5 /* Changelog: v1.1: @@ -52,6 +52,8 @@ (Not released) v1.4.2: Version for ZDoom. + v1.5: + Optimizations */ @@ -78,152 +80,17 @@ #include #include "nukedopl3.h" -// Channel types - -enum { - ch_4op2, - ch_2op, - ch_4op, - ch_drum -}; - -// Envelope generator states - -enum { - eg_off, - eg_attack, - eg_decay, - eg_sustain, - eg_release -}; - -// Envelope key types - -enum { - egk_norm = 1, - egk_drum = 2 -}; - -// -// logsin table -// - -static const Bit16u logsinrom[256] = { - 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, - 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, - 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, - 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, - 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, - 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, - 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, - 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, - 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, - 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, - 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, - 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, - 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, - 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, - 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, - 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 -}; - -// -// exp table -// - -static const Bit16u exprom[256] = { - 0x000, 0x003, 0x006, 0x008, 0x00b, 0x00e, 0x011, 0x014, 0x016, 0x019, 0x01c, 0x01f, 0x022, 0x025, 0x028, 0x02a, - 0x02d, 0x030, 0x033, 0x036, 0x039, 0x03c, 0x03f, 0x042, 0x045, 0x048, 0x04b, 0x04e, 0x051, 0x054, 0x057, 0x05a, - 0x05d, 0x060, 0x063, 0x066, 0x069, 0x06c, 0x06f, 0x072, 0x075, 0x078, 0x07b, 0x07e, 0x082, 0x085, 0x088, 0x08b, - 0x08e, 0x091, 0x094, 0x098, 0x09b, 0x09e, 0x0a1, 0x0a4, 0x0a8, 0x0ab, 0x0ae, 0x0b1, 0x0b5, 0x0b8, 0x0bb, 0x0be, - 0x0c2, 0x0c5, 0x0c8, 0x0cc, 0x0cf, 0x0d2, 0x0d6, 0x0d9, 0x0dc, 0x0e0, 0x0e3, 0x0e7, 0x0ea, 0x0ed, 0x0f1, 0x0f4, - 0x0f8, 0x0fb, 0x0ff, 0x102, 0x106, 0x109, 0x10c, 0x110, 0x114, 0x117, 0x11b, 0x11e, 0x122, 0x125, 0x129, 0x12c, - 0x130, 0x134, 0x137, 0x13b, 0x13e, 0x142, 0x146, 0x149, 0x14d, 0x151, 0x154, 0x158, 0x15c, 0x160, 0x163, 0x167, - 0x16b, 0x16f, 0x172, 0x176, 0x17a, 0x17e, 0x181, 0x185, 0x189, 0x18d, 0x191, 0x195, 0x199, 0x19c, 0x1a0, 0x1a4, - 0x1a8, 0x1ac, 0x1b0, 0x1b4, 0x1b8, 0x1bc, 0x1c0, 0x1c4, 0x1c8, 0x1cc, 0x1d0, 0x1d4, 0x1d8, 0x1dc, 0x1e0, 0x1e4, - 0x1e8, 0x1ec, 0x1f0, 0x1f5, 0x1f9, 0x1fd, 0x201, 0x205, 0x209, 0x20e, 0x212, 0x216, 0x21a, 0x21e, 0x223, 0x227, - 0x22b, 0x230, 0x234, 0x238, 0x23c, 0x241, 0x245, 0x249, 0x24e, 0x252, 0x257, 0x25b, 0x25f, 0x264, 0x268, 0x26d, - 0x271, 0x276, 0x27a, 0x27f, 0x283, 0x288, 0x28c, 0x291, 0x295, 0x29a, 0x29e, 0x2a3, 0x2a8, 0x2ac, 0x2b1, 0x2b5, - 0x2ba, 0x2bf, 0x2c4, 0x2c8, 0x2cd, 0x2d2, 0x2d6, 0x2db, 0x2e0, 0x2e5, 0x2e9, 0x2ee, 0x2f3, 0x2f8, 0x2fd, 0x302, - 0x306, 0x30b, 0x310, 0x315, 0x31a, 0x31f, 0x324, 0x329, 0x32e, 0x333, 0x338, 0x33d, 0x342, 0x347, 0x34c, 0x351, - 0x356, 0x35b, 0x360, 0x365, 0x36a, 0x370, 0x375, 0x37a, 0x37f, 0x384, 0x38a, 0x38f, 0x394, 0x399, 0x39f, 0x3a4, - 0x3a9, 0x3ae, 0x3b4, 0x3b9, 0x3bf, 0x3c4, 0x3c9, 0x3cf, 0x3d4, 0x3da, 0x3df, 0x3e4, 0x3ea, 0x3ef, 0x3f5, 0x3fa -}; - -// -// freq mult table multiplied by 2 -// -// 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 12, 12, 15, 15 -// - -static const Bit8u mt[16] = { 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 }; - -// -// ksl table -// - -static const Bit8u kslrom[16] = { 0, 64, 80, 90, 96, 102, 106, 110, 112, 116, 118, 120, 122, 124, 126, 127 }; - -static const Bit8u kslshift[4] = { 8, 1, 2, 0 }; - -// -// LFO vibrato -// - -static const Bit8u vib_table[8] = { 3, 1, 0, 1, 3, 1, 0, 1 }; -static const Bit8s vibsgn_table[8] = { 1, 1, 1, 1, -1, -1, -1, -1 }; - -// -// envelope generator constants -// - -static const Bit8u eg_incstep[3][4][8] = { - { { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 } }, - { { 0, 1, 0, 1, 0, 1, 0, 1 }, { 1, 1, 0, 1, 0, 1, 0, 1 }, { 1, 1, 0, 1, 1, 1, 0, 1 }, { 1, 1, 1, 1, 1, 1, 0, 1 } }, - { { 1, 1, 1, 1, 1, 1, 1, 1 }, { 2, 2, 1, 1, 1, 1, 1, 1 }, { 2, 2, 1, 1, 2, 2, 1, 1 }, { 2, 2, 2, 2, 2, 2, 1, 1 } } -}; - -// -// address decoding -// - -static const Bit8s ad_slot[0x20] = { 0, 2, 4, 1, 3, 5, -1, -1, 6, 8, 10, 7, 9, 11, -1, -1, 12, 14, 16, 13, 15, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; - -static const Bit8u op_offset[18] = { 0x00, 0x03, 0x01, 0x04, 0x02, 0x05, 0x08, 0x0b, 0x09, 0x0c, 0x0a, 0x0d, 0x10, 0x13, 0x11, 0x14, 0x12, 0x15 }; - - - -static const Bit8u eg_incdesc[16] = { - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2 -}; - -static const Bit8s eg_incsh[16] = { - 0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, -1, -2 -}; - -typedef Bit16s(*envelope_sinfunc)(Bit16u phase, Bit16u envelope); -typedef void(*envelope_genfunc)(slot *slott); - -// -// Phase generator -// - -void PG_Generate(chip *opl, Bit8u op) { - slot *slt = &opl->OPs[op]; - channel *chan = &opl->Channels[op / 2]; - Bit16u fnum = chan->f_number; - if (slt->vibrato) { - Bit8u fnum_high = chan->f_number >> (7 + vib_table[opl->vib_pos] + (!opl->dvb)); - fnum += fnum_high * vibsgn_table[opl->vib_pos]; - } - slt->PG_pos += (((fnum << chan->block) >> 1) * mt[slt->mult]) >> 1; -} - // // Envelope generator // +typedef Bit16s(*envelope_sinfunc)(Bit16u phase, Bit16u envelope); +typedef void(*envelope_genfunc)(opl_slot *slott); + Bit16s envelope_calcexp(Bit32u level) { + if (level > 0x1fff) { + level = 0x1fff; + } return ((exprom[(level & 0xff) ^ 0xff] | 0x400) << 1) >> (level >> 8); } @@ -352,12 +219,12 @@ envelope_sinfunc envelope_sin[8] = { envelope_calcsin7 }; -void envelope_gen_off(slot *slott); -void envelope_gen_change(slot *slott); -void envelope_gen_attack(slot *slott); -void envelope_gen_decay(slot *slott); -void envelope_gen_sustain(slot *slott); -void envelope_gen_release(slot *slott); +void envelope_gen_off(opl_slot *slott); +void envelope_gen_change(opl_slot *slott); +void envelope_gen_attack(opl_slot *slott); +void envelope_gen_decay(opl_slot *slott); +void envelope_gen_sustain(opl_slot *slott); +void envelope_gen_release(opl_slot *slott); envelope_genfunc envelope_gen[6] = { envelope_gen_off, @@ -377,222 +244,215 @@ enum envelope_gen_num { envelope_gen_num_change = 5 }; -void envelope_gen_off(slot *slott) { - slott->EG_out = 0x1ff; -} - -void envelope_gen_change(slot *slott) { - slott->eg_gen = slott->eg_gennext; -} - -void envelope_gen_attack(slot *slott) { - slott->EG_out += ((~slott->EG_out) *slott->eg_inc) >> 3; - if (slott->EG_out < 0x00) { - slott->EG_out = 0x00; - } - if (slott->EG_out == 0x00) { - slott->eg_gen = envelope_gen_num_change; - slott->eg_gennext = envelope_gen_num_decay; - } -} - -void envelope_gen_decay(slot *slott) { - slott->EG_out += slott->eg_inc; - if (slott->EG_out >= slott->EG_sl << 4) { - slott->eg_gen = envelope_gen_num_change; - slott->eg_gennext = envelope_gen_num_sustain; - } -} - -void envelope_gen_sustain(slot *slott) { - if (!slott->EG_type) { - envelope_gen_release(slott); - } -} - -void envelope_gen_release(slot *slott) { - slott->EG_out += slott->eg_inc; - if (slott->EG_out >= 0x1ff) { - slott->eg_gen = envelope_gen_num_change; - slott->eg_gennext = envelope_gen_num_off; - } -} - -Bit8u EG_CalcRate(chip *opl, Bit8u op, Bit8u rate) { - slot *slt = &opl->OPs[op]; - channel *chan = &opl->Channels[op / 2]; - if (rate == 0x00) { +Bit8u envelope_calc_rate(opl_slot *slot, Bit8u reg_rate) { + if (reg_rate == 0x00) { return 0x00; } - Bit8u rof = slt->ksr ? chan->ksv : (chan->ksv >> 2); - Bit8u rat = (rate << 2) + rof; - if (rat > 0x3c) { - rat = 0x3c; + Bit8u rate = (reg_rate << 2) + (slot->reg_ksr ? slot->channel->ksv : (slot->channel->ksv >> 2)); + if (rate > 0x3c) { + rate = 0x3c; } - return rat; + return rate; } -void envelope_calc(chip *opl, Bit8u op) { - slot *slott = &opl->OPs[op]; - Bit16u timer = opl->timer; - Bit8u rate_h, rate_l; - Bit8u rate; - Bit8u reg_rate = 0;; - switch (slott->eg_gen) { +void envelope_update_ksl(opl_slot *slot) { + Bit16s ksl = (kslrom[slot->channel->f_num >> 6] << 1) - ((slot->channel->block ^ 0x07) << 5) - 0x20; + if (ksl < 0) { + ksl = 0; + } + slot->eg_ksl = (Bit8u)ksl; +} + +void envelope_update_rate(opl_slot *slot) { + switch (slot->eg_gen) { + case envelope_gen_num_off: + slot->eg_rate = 0; + break; case envelope_gen_num_attack: - reg_rate = slott->EG_ar; + slot->eg_rate = envelope_calc_rate(slot, slot->reg_ar); break; case envelope_gen_num_decay: - reg_rate = slott->EG_dr; + slot->eg_rate = envelope_calc_rate(slot, slot->reg_dr); break; case envelope_gen_num_sustain: case envelope_gen_num_release: - reg_rate = slott->EG_rr; + slot->eg_rate = envelope_calc_rate(slot, slot->reg_rr); break; } - rate = EG_CalcRate(opl, op, reg_rate); - rate_h = rate >> 2; - rate_l = rate & 3; +} + +void envelope_gen_off(opl_slot *slot) { + slot->eg_rout = 0x1ff; +} + +void envelope_gen_change(opl_slot *slot) { + slot->eg_gen = slot->eg_gennext; + envelope_update_rate(slot); +} + +void envelope_gen_attack(opl_slot *slot) { + slot->eg_rout += ((~slot->eg_rout) *slot->eg_inc) >> 3; + if (slot->eg_rout < 0x00) { + slot->eg_rout = 0x00; + } + if (!slot->eg_rout) { + slot->eg_gen = envelope_gen_num_change; + slot->eg_gennext = envelope_gen_num_decay; + } +} + +void envelope_gen_decay(opl_slot *slot) { + slot->eg_rout += slot->eg_inc; + if (slot->eg_rout >= slot->reg_sl << 4) { + slot->eg_gen = envelope_gen_num_change; + slot->eg_gennext = envelope_gen_num_sustain; + } +} + +void envelope_gen_sustain(opl_slot *slot) { + if (!slot->reg_type) { + envelope_gen_release(slot); + } +} + +void envelope_gen_release(opl_slot *slot) { + slot->eg_rout += slot->eg_inc; + if (slot->eg_rout >= 0x1ff) { + slot->eg_gen = envelope_gen_num_change; + slot->eg_gennext = envelope_gen_num_off; + } +} + +void envelope_calc(opl_slot *slot) { + Bit8u rate_h, rate_l; + rate_h = slot->eg_rate >> 2; + rate_l = slot->eg_rate & 3; Bit8u inc = 0; - if (slott->eg_gen == envelope_gen_num_attack && rate_h == 0x0f) { + if (slot->eg_gen == envelope_gen_num_attack && rate_h == 0x0f) { inc = 8; } else if (eg_incsh[rate_h] > 0) { - if ((timer & ((1 << eg_incsh[rate_h]) - 1)) == 0) { - inc = eg_incstep[eg_incdesc[rate_h]][rate_l][((timer) >> eg_incsh[rate_h]) & 0x07]; + if ((slot->chip->timer & ((1 << eg_incsh[rate_h]) - 1)) == 0) { + inc = eg_incstep[eg_incdesc[rate_h]][rate_l][((slot->chip->timer) >> eg_incsh[rate_h]) & 0x07]; } } else { - inc = eg_incstep[eg_incdesc[rate_h]][rate_l][timer & 0x07] << (-eg_incsh[rate_h]); + inc = eg_incstep[eg_incdesc[rate_h]][rate_l][slot->chip->timer & 0x07] << (-eg_incsh[rate_h]); } - slott->eg_inc = inc; - envelope_gen[slott->eg_gen](slott); + slot->eg_inc = inc; + envelope_gen[slot->eg_gen](slot); + slot->eg_out = slot->eg_rout + (slot->reg_tl << 2) + (slot->eg_ksl >> kslshift[slot->reg_ksl]) + *slot->trem; } -void EG_UpdateKSL(chip *opl, Bit8u op) { - slot *slt = &opl->OPs[op]; - channel *chan = &opl->Channels[op / 2]; - Bit8u fnum_high = (chan->f_number >> 6) & 0x0f; - Bit16s ksl = (kslrom[fnum_high] << 1) - ((chan->block ^ 0x07) << 5) - 0x20; - if (ksl < 0x00) { - ksl = 0x00; +void eg_keyon(opl_slot *slot, Bit8u type) { + if (!slot->key) { + slot->eg_gen = envelope_gen_num_change; + slot->eg_gennext = envelope_gen_num_attack; + slot->pg_phase = 0x00; } - slt->EG_ksl = ksl >> kslshift[slt->ksl]; + slot->key |= type; } -void EG_Generate(chip *opl, Bit8u op) { - slot *slt = &opl->OPs[op]; - envelope_calc(opl, op); - slt->EG_mout = slt->EG_out + slt->EG_ksl + (slt->EG_tl << 2); - if (slt->tremolo) { - slt->EG_mout += opl->trem_val; - } - if (slt->EG_mout > 0x1ff) { - slt->EG_mout = 0x1ff; - } -} - -void EG_KeyOn(chip *opl, Bit8u op, Bit8u type) { - slot *slt = &opl->OPs[op]; - if (!slt->key) { - slt->EG_state = eg_attack; - slt->eg_gen = envelope_gen_num_change; - slt->eg_gennext = envelope_gen_num_attack; - slt->PG_pos = 0; - } - slt->key |= type; -} - -void EG_KeyOff(chip *opl, Bit8u op, Bit8u type) { - slot *slt = &opl->OPs[op]; - if (slt->key) { - slt->key &= (~type); - if (slt->key == 0x00) { - slt->EG_state = eg_release; - slt->eg_gen = envelope_gen_num_change; - slt->eg_gennext = envelope_gen_num_release; +void eg_keyoff(opl_slot *slot, Bit8u type) { + if (slot->key) { + slot->key &= (~type); + if (!slot->key) { + slot->eg_gen = envelope_gen_num_change; + slot->eg_gennext = envelope_gen_num_release; } } } +// +// Phase Generator +// + +void pg_generate(opl_slot *slot) { + Bit16u f_num = slot->channel->f_num; + if (slot->reg_vib) { + Bit8u f_num_high = f_num >> (7 + vib_table[(slot->chip->timer >> 10)&0x07] + (0x01 - slot->chip->dvb)); + f_num += f_num_high * vibsgn_table[(slot->chip->timer >> 10) & 0x07]; + } + slot->pg_phase += (((f_num << slot->channel->block) >> 1) * mt[slot->reg_mult]) >> 1; +} + // // Noise Generator // -void N_Generate(chip *opl) { - if (opl->noise & 1) { - opl->noise ^= 0x800302; +void n_generate(opl_chip *chip) { + if (chip->noise & 0x01) { + chip->noise ^= 0x800302; } - opl->noise >>= 1; + chip->noise >>= 1; } // -// Operator(Slot) +// Slot // -void OP_Update20(chip *opl, Bit8u op) { - slot *slt = &opl->OPs[op]; - slt->tremolo = (opl->opl_memory[0x20 + slt->offset] >> 7); - slt->vibrato = (opl->opl_memory[0x20 + slt->offset] >> 6) & 0x01; - slt->EG_type = (opl->opl_memory[0x20 + slt->offset] >> 5) & 0x01; - slt->ksr = (opl->opl_memory[0x20 + slt->offset] >> 4) & 0x01; - slt->mult = (opl->opl_memory[0x20 + slt->offset]) & 0x0f; +void slot_write20(opl_slot *slot,Bit8u data) { + if ((data >> 7) & 0x01) { + slot->trem = &slot->chip->tremval; + } + else { + slot->trem = (Bit8u*)&slot->chip->zeromod; + } + slot->reg_vib = (data >> 6) & 0x01; + slot->reg_type = (data >> 5) & 0x01; + slot->reg_ksr = (data >> 4) & 0x01; + slot->reg_mult = data & 0x0f; + envelope_update_rate(slot); } -void OP_Update40(chip *opl, Bit8u op) { - slot *slt = &opl->OPs[op]; - slt->EG_tl = (opl->opl_memory[0x40 + slt->offset]) & 0x3f; - slt->ksl = (opl->opl_memory[0x40 + slt->offset] >> 6) & 0x03; - EG_UpdateKSL(opl, op); +void slot_write40(opl_slot *slot, Bit8u data) { + slot->reg_ksl = (data >> 6) & 0x03; + slot->reg_tl = data & 0x3f; + envelope_update_ksl(slot); } -void OP_Update60(chip *opl, Bit8u op) { - slot *slt = &opl->OPs[op]; - slt->EG_dr = (opl->opl_memory[0x60 + slt->offset]) & 0x0f; - slt->EG_ar = (opl->opl_memory[0x60 + slt->offset] >> 4) & 0x0f; +void slot_write60(opl_slot *slot, Bit8u data) { + slot->reg_ar = (data >> 4) & 0x0f; + slot->reg_dr = data & 0x0f; + envelope_update_rate(slot); } -void OP_Update80(chip *opl, Bit8u op) { - slot *slt = &opl->OPs[op]; - slt->EG_rr = (opl->opl_memory[0x80 + slt->offset]) & 0x0f; - slt->EG_sl = (opl->opl_memory[0x80 + slt->offset] >> 4) & 0x0f; - if (slt->EG_sl == 0x0f) { - slt->EG_sl = 0x1f; +void slot_write80(opl_slot *slot, Bit8u data) { + slot->reg_sl = (data >> 4) & 0x0f; + if (slot->reg_sl == 0x0f) { + slot->reg_sl = 0x1f; + } + slot->reg_rr = data & 0x0f; + envelope_update_rate(slot); +} + +void slot_writee0(opl_slot *slot, Bit8u data) { + slot->reg_wf = data & 0x07; + if (slot->chip->newm == 0x00) { + slot->reg_wf &= 0x03; } } -void OP_UpdateE0(chip *opl, Bit8u op) { - slot *slt = &opl->OPs[op]; - slt->waveform = opl->opl_memory[0xe0 + slt->offset] & 0x07; - if (!opl->newm) { - slt->waveform &= 0x03; +void slot_generatephase(opl_slot *slot, Bit16u phase) { + slot->out = envelope_sin[slot->reg_wf](phase, slot->eg_out); +} + +void slot_generate(opl_slot *slot) { + slot->out = envelope_sin[slot->reg_wf]((slot->pg_phase >> 9) + (*slot->mod), slot->eg_out); +} + +void slot_generatezm(opl_slot *slot) { + slot->out = envelope_sin[slot->reg_wf]((slot->pg_phase >> 9), slot->eg_out); +} + +void slot_calgfb(opl_slot *slot) { + slot->prout[1] = slot->prout[0]; + slot->prout[0] = slot->out; + if (slot->channel->fb != 0x00) { + slot->fbmod = (slot->prout[0] + slot->prout[1]) >> (0x09 - slot->channel->fb); } -} - -void OP_GeneratePhase(chip *opl, Bit8u op, Bit16u phase) { - slot *slt = &opl->OPs[op]; - slt->out = envelope_sin[slt->waveform](phase, slt->EG_out); -} - -void OP_Generate(chip *opl, Bit8u op) { - slot *slt = &opl->OPs[op]; - slt->out = envelope_sin[slt->waveform]((Bit16u)((slt->PG_pos >> 9) + (*slt->mod)), slt->EG_mout); -} - -void OP_GenerateZM(chip *opl, Bit8u op) { - slot *slt = &opl->OPs[op]; - slt->out = envelope_sin[slt->waveform]((Bit16u)(slt->PG_pos >> 9), slt->EG_mout); -} - -void OP_CalcFB(chip *opl, Bit8u op) { - slot *slt = &opl->OPs[op]; - channel *chan = &opl->Channels[op / 2]; - slt->prevout[1] = slt->prevout[0]; - slt->prevout[0] = slt->out; - if (chan->feedback) { - slt->fbmod = (slt->prevout[0] + slt->prevout[1]) >> chan->feedback; - } else { - slt->fbmod = 0; + else { + slot->fbmod = 0; } } @@ -600,301 +460,319 @@ void OP_CalcFB(chip *opl, Bit8u op) { // Channel // -void CH_UpdateRhythm(chip *opl) { - opl->rhythm = (opl->opl_memory[0xbd] & 0x3f); - if (opl->rhythm & 0x20) { - for (Bit8u i = 6; i < 9; i++) { - opl->Channels[i].chtype = ch_drum; +void chan_setupalg(opl_channel *channel); + +void chan_updaterhythm(opl_chip *chip, Bit8u data) { + chip->rhy = data & 0x3f; + if (chip->rhy & 0x20) { + chip->channel[6].out[0] = &chip->slot[13].out; + chip->channel[6].out[1] = &chip->slot[13].out; + chip->channel[6].out[2] = &chip->zeromod; + chip->channel[6].out[3] = &chip->zeromod; + chip->channel[7].out[0] = &chip->slot[14].out; + chip->channel[7].out[1] = &chip->slot[14].out; + chip->channel[7].out[2] = &chip->slot[15].out; + chip->channel[7].out[3] = &chip->slot[15].out; + chip->channel[8].out[0] = &chip->slot[16].out; + chip->channel[8].out[1] = &chip->slot[16].out; + chip->channel[8].out[2] = &chip->slot[17].out; + chip->channel[8].out[3] = &chip->slot[17].out; + for (Bit8u chnum = 6; chnum < 9; chnum++) { + chip->channel[chnum].chtype = ch_drum; } - //HH - if (opl->rhythm & 0x01) { - EG_KeyOn(opl, 14, egk_drum); - } else { - EG_KeyOff(opl, 14, egk_drum); + //hh + if (chip->rhy & 0x01) { + eg_keyon(&chip->slot[14], egk_drum); } - //TC - if (opl->rhythm & 0x02) { - EG_KeyOn(opl, 17, egk_drum); - } else { - EG_KeyOff(opl, 17, egk_drum); + else { + eg_keyoff(&chip->slot[14], egk_drum); } - //TOM - if (opl->rhythm & 0x04) { - EG_KeyOn(opl, 16, egk_drum); - } else { - EG_KeyOff(opl, 16, egk_drum); + //tc + if (chip->rhy & 0x02) { + eg_keyon(&chip->slot[17], egk_drum); } - //SD - if (opl->rhythm & 0x08) { - EG_KeyOn(opl, 15, egk_drum); - } else { - EG_KeyOff(opl, 15, egk_drum); + else { + eg_keyoff(&chip->slot[17], egk_drum); } - //BD - if (opl->rhythm & 0x10) { - EG_KeyOn(opl, 12, egk_drum); - EG_KeyOn(opl, 13, egk_drum); - } else { - EG_KeyOff(opl, 12, egk_drum); - EG_KeyOff(opl, 13, egk_drum); + //tom + if (chip->rhy & 0x04) { + eg_keyon(&chip->slot[16], egk_drum); } - } else { - for (Bit8u i = 6; i < 9; i++) { - opl->Channels[i].chtype = ch_2op; + else { + eg_keyoff(&chip->slot[16], egk_drum); + } + //sd + if (chip->rhy & 0x08) { + eg_keyon(&chip->slot[15], egk_drum); + } + else { + eg_keyoff(&chip->slot[15], egk_drum); + } + //bd + if (chip->rhy & 0x10) { + eg_keyon(&chip->slot[12], egk_drum); + eg_keyon(&chip->slot[13], egk_drum); + } + else { + eg_keyoff(&chip->slot[12], egk_drum); + eg_keyoff(&chip->slot[13], egk_drum); + } + } + else { + for (Bit8u chnum = 6; chnum < 9; chnum++) { + chip->channel[chnum].chtype = ch_2op; + chan_setupalg(&chip->channel[chnum]); } } } -void CH_UpdateAB0(chip *opl, Bit8u ch) { - channel *chan = &opl->Channels[ch]; - if (opl->newm && chan->chtype == ch_4op2) { +void chan_writea0(opl_channel *channel, Bit8u data) { + if (channel->chip->newm && channel->chtype == ch_4op2) { return; } - Bit16u f_number = (opl->opl_memory[0xa0 + chan->offset]) | (((opl->opl_memory[0xb0 + chan->offset]) & 0x03) << 8); - Bit8u block = ((opl->opl_memory[0xb0 + chan->offset]) >> 2) & 0x07; - Bit8u ksv = block * 2 | ((f_number >> (9 - opl->nts)) & 0x01); - chan->f_number = f_number; - chan->block = block; - chan->ksv = ksv; - EG_UpdateKSL(opl, ch * 2); - EG_UpdateKSL(opl, ch * 2 + 1); - OP_Update60(opl, ch * 2); - OP_Update60(opl, ch * 2 + 1); - OP_Update80(opl, ch * 2); - OP_Update80(opl, ch * 2 + 1); - if (opl->newm && chan->chtype == ch_4op) { - chan = &opl->Channels[ch + 3]; - chan->f_number = f_number; - chan->block = block; - chan->ksv = ksv; - EG_UpdateKSL(opl, (ch + 3) * 2); - EG_UpdateKSL(opl, (ch + 3) * 2 + 1); - OP_Update60(opl, (ch + 3) * 2); - OP_Update60(opl, (ch + 3) * 2 + 1); - OP_Update80(opl, (ch + 3) * 2); - OP_Update80(opl, (ch + 3) * 2 + 1); + channel->f_num = (channel->f_num & 0x300) | data; + channel->ksv = (channel->block << 1) | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); + envelope_update_ksl(channel->slots[0]); + envelope_update_ksl(channel->slots[1]); + envelope_update_rate(channel->slots[0]); + envelope_update_rate(channel->slots[1]); + if (channel->chip->newm && channel->chtype == ch_4op) { + channel->pair->f_num = channel->f_num; + channel->pair->ksv = channel->ksv; + envelope_update_ksl(channel->pair->slots[0]); + envelope_update_ksl(channel->pair->slots[1]); + envelope_update_rate(channel->pair->slots[0]); + envelope_update_rate(channel->pair->slots[1]); } } -void CH_SetupAlg(chip *opl, Bit8u ch) { - channel *chan = &opl->Channels[ch]; - if (chan->alg & 0x08) { +void chan_writeb0(opl_channel *channel, Bit8u data) { + if (channel->chip->newm && channel->chtype == ch_4op2) { return; } - if (chan->alg & 0x04) { - switch (chan->alg & 0x03) { - case 0: - opl->OPs[(ch - 3) * 2].mod = &opl->OPs[(ch - 3) * 2].fbmod; - opl->OPs[(ch - 3) * 2 + 1].mod = &opl->OPs[(ch - 3) * 2].out; - opl->OPs[ch * 2].mod = &opl->OPs[(ch - 3) * 2 + 1].out; - opl->OPs[ch * 2 + 1].mod = &opl->OPs[ch * 2].out; - break; - case 1: - opl->OPs[(ch - 3) * 2].mod = &opl->OPs[(ch - 3) * 2].fbmod; - opl->OPs[(ch - 3) * 2 + 1].mod = &opl->OPs[(ch - 3) * 2].out; - opl->OPs[ch * 2].mod = &opl->zm; - opl->OPs[ch * 2 + 1].mod = &opl->OPs[ch * 2].out; - break; - case 2: - opl->OPs[(ch - 3) * 2].mod = &opl->OPs[(ch - 3) * 2].fbmod; - opl->OPs[(ch - 3) * 2 + 1].mod = &opl->zm; - opl->OPs[ch * 2].mod = &opl->OPs[(ch - 3) * 2 + 1].out; - opl->OPs[ch * 2 + 1].mod = &opl->OPs[ch * 2].out; - break; - case 3: - opl->OPs[(ch - 3) * 2].mod = &opl->OPs[(ch - 3) * 2].fbmod; - opl->OPs[(ch - 3) * 2 + 1].mod = &opl->zm; - opl->OPs[ch * 2].mod = &opl->OPs[(ch - 3) * 2 + 1].out; - opl->OPs[ch * 2 + 1].mod = &opl->zm; - break; - } - } else { - switch (chan->alg & 0x01) { - case 0: - opl->OPs[ch * 2].mod = &opl->OPs[ch * 2].fbmod; - opl->OPs[ch * 2 + 1].mod = &opl->OPs[ch * 2].out; - break; - case 1: - opl->OPs[ch * 2].mod = &opl->OPs[ch * 2].fbmod; - opl->OPs[ch * 2 + 1].mod = &opl->zm; - break; - } + channel->f_num = (channel->f_num & 0xff) | ((data & 0x03) << 8); + channel->block = (data >> 2) & 0x07; + channel->ksv = (channel->block << 1) | ((channel->f_num >> (0x09 - channel->chip->nts)) & 0x01); + envelope_update_ksl(channel->slots[0]); + envelope_update_ksl(channel->slots[1]); + envelope_update_rate(channel->slots[0]); + envelope_update_rate(channel->slots[1]); + if (channel->chip->newm && channel->chtype == ch_4op) { + channel->pair->f_num = channel->f_num; + channel->pair->block = channel->block; + channel->pair->ksv = channel->ksv; + envelope_update_ksl(channel->pair->slots[0]); + envelope_update_ksl(channel->pair->slots[1]); + envelope_update_rate(channel->pair->slots[0]); + envelope_update_rate(channel->pair->slots[1]); } } -void CH_UpdateC0(chip *opl, Bit8u ch) { - channel *chan = &opl->Channels[ch]; - Bit8u fb = (opl->opl_memory[0xc0 + chan->offset] & 0x0e) >> 1; - chan->feedback = fb ? (9 - fb) : 0; - chan->con = opl->opl_memory[0xc0 + chan->offset] & 0x01; - chan->alg = chan->con; - if (opl->newm) { - if (chan->chtype == ch_4op) { - channel *chan1 = &opl->Channels[ch + 3]; - chan1->alg = 0x04 | (chan->con << 1) | (chan1->con); - chan->alg = 0x08; - CH_SetupAlg(opl, ch + 3); - } else if (chan->chtype == ch_4op2) { - channel *chan1 = &opl->Channels[ch - 3]; - chan->alg = 0x04 | (chan1->con << 1) | (chan->con); - chan1->alg = 0x08; - CH_SetupAlg(opl, ch); - } else { - CH_SetupAlg(opl, ch); - } - } else { - CH_SetupAlg(opl, ch); - } - if (opl->newm) { - chan->cha = ((opl->opl_memory[0xc0 + chan->offset] >> 4) & 0x01) ? ~0 : 0; - chan->chb = ((opl->opl_memory[0xc0 + chan->offset] >> 5) & 0x01) ? ~0 : 0; - chan->chc = ((opl->opl_memory[0xc0 + chan->offset] >> 6) & 0x01) ? ~0 : 0; - chan->chd = ((opl->opl_memory[0xc0 + chan->offset] >> 7) & 0x01) ? ~0 : 0; - } else { - opl->Channels[ch].cha = opl->Channels[ch].chb = ~0; - opl->Channels[ch].chc = opl->Channels[ch].chd = 0; - } -} - -void CH_Set2OP(chip *opl) { - for (Bit8u i = 0; i < 18; i++) { - opl->Channels[i].chtype = ch_2op; - CH_UpdateC0(opl, i); - } -} - -void CH_Set4OP(chip *opl) { - for (Bit8u i = 0; i < 3; i++) { - if ((opl->opl_memory[0x104] >> i) & 0x01) { - opl->Channels[i].chtype = ch_4op; - opl->Channels[i + 3].chtype = ch_4op2; - CH_UpdateC0(opl, i); - CH_UpdateC0(opl, i + 3); - } - if ((opl->opl_memory[0x104] >> (i + 3)) & 0x01) { - opl->Channels[i + 9].chtype = ch_4op; - opl->Channels[i + 3 + 9].chtype = ch_4op2; - CH_UpdateC0(opl, i + 9); - CH_UpdateC0(opl, i + 3 + 9); - } - } -} - -void CH_GenerateRhythm(chip *opl) { - if (opl->rhythm & 0x20) { - channel *chan6 = &opl->Channels[6]; - channel *chan7 = &opl->Channels[7]; - channel *chan8 = &opl->Channels[8]; - slot *slt12 = &opl->OPs[12]; - slot *slt13 = &opl->OPs[13]; - slot *slt14 = &opl->OPs[14]; - slot *slt15 = &opl->OPs[15]; - slot *slt16 = &opl->OPs[16]; - slot *slt17 = &opl->OPs[17]; - //BD - OP_Generate(opl, 12); - OP_Generate(opl, 13); - chan6->out = slt13->out * 2; - Bit16u P14 = (slt14->PG_pos >> 9) & 0x3ff; - Bit16u P17 = (slt17->PG_pos >> 9) & 0x3ff; - Bit16u phase = 0; - // HH TC Phase bit - Bit16u PB = ((P14 & 0x08) | (((P14 >> 5) ^ P14) & 0x04) | (((P17 >> 2) ^ P17) & 0x08)) ? 0x01 : 0x00; - //HH - phase = (PB << 9) | (0x34 << ((PB ^ (opl->noise & 0x01) << 1))); - OP_GeneratePhase(opl, 14, phase); - //SD - phase = (0x100 << ((P14 >> 8) & 0x01)) ^ ((opl->noise & 0x01) << 8); - OP_GeneratePhase(opl, 15, phase); - //TT - OP_GenerateZM(opl, 16); - //TC - phase = 0x100 | (PB << 9); - OP_GeneratePhase(opl, 17, phase); - chan7->out = (slt14->out + slt15->out) * 2; - chan8->out = (slt16->out + slt17->out) * 2; - } -} - -void CH_Generate(chip *opl, Bit8u ch) { - channel *chan = &opl->Channels[ch]; - if (chan->chtype == ch_drum) { +void chan_setupalg(opl_channel *channel) { + if (channel->chtype == ch_drum) { return; } - if (chan->alg & 0x08) { - chan->out = 0; + if (channel->alg & 0x08) { return; - } else if (chan->alg & 0x04) { - OP_Generate(opl, (ch - 3) * 2); - OP_Generate(opl, (ch - 3) * 2 + 1); - OP_Generate(opl, ch * 2); - OP_Generate(opl, ch * 2 + 1); - switch (chan->alg & 0x03) { - case 0: - chan->out = opl->OPs[ch * 2 + 1].out; + } + if (channel->alg & 0x04) { + channel->pair->out[0] = &channel->chip->zeromod; + channel->pair->out[1] = &channel->chip->zeromod; + channel->pair->out[2] = &channel->chip->zeromod; + channel->pair->out[3] = &channel->chip->zeromod; + switch (channel->alg & 0x03) { + case 0x00: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->pair->slots[0]->out; + channel->slots[0]->mod = &channel->pair->slots[1]->out; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->slots[1]->out; + channel->out[1] = &channel->chip->zeromod; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; break; - case 1: - chan->out = opl->OPs[(ch - 3) * 2 + 1].out + opl->OPs[ch * 2 + 1].out; + case 0x01: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->pair->slots[0]->out; + channel->slots[0]->mod = &channel->chip->zeromod; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->pair->slots[1]->out; + channel->out[1] = &channel->slots[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; break; - case 2: - chan->out = opl->OPs[(ch - 3) * 2].out + opl->OPs[ch * 2 + 1].out; + case 0x02: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->chip->zeromod; + channel->slots[0]->mod = &channel->pair->slots[1]->out; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->pair->slots[0]->out; + channel->out[1] = &channel->slots[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; break; - case 3: - chan->out = opl->OPs[(ch - 3) * 2].out + opl->OPs[ch * 2].out + opl->OPs[ch * 2 + 1].out; + case 0x03: + channel->pair->slots[0]->mod = &channel->pair->slots[0]->fbmod; + channel->pair->slots[1]->mod = &channel->chip->zeromod; + channel->slots[0]->mod = &channel->pair->slots[1]->out; + channel->slots[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->pair->slots[0]->out; + channel->out[1] = &channel->slots[0]->out; + channel->out[2] = &channel->slots[1]->out; + channel->out[3] = &channel->chip->zeromod; break; } } else { - OP_Generate(opl, ch * 2); - OP_Generate(opl, ch * 2 + 1); - switch (chan->alg & 0x01) { - case 0: - chan->out = opl->OPs[ch * 2 + 1].out; + switch (channel->alg & 0x01) { + case 0x00: + channel->slots[0]->mod = &channel->slots[0]->fbmod; + channel->slots[1]->mod = &channel->slots[0]->out; + channel->out[0] = &channel->slots[1]->out; + channel->out[1] = &channel->chip->zeromod; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; break; - case 1: - chan->out = opl->OPs[ch * 2].out + opl->OPs[ch * 2 + 1].out; + case 0x01: + channel->slots[0]->mod = &channel->slots[0]->fbmod; + channel->slots[1]->mod = &channel->chip->zeromod; + channel->out[0] = &channel->slots[0]->out; + channel->out[1] = &channel->slots[1]->out; + channel->out[2] = &channel->chip->zeromod; + channel->out[3] = &channel->chip->zeromod; break; } } } -void CH_Enable(chip *opl, Bit8u ch) { - channel *chan = &opl->Channels[ch]; - if (opl->newm) { - if (chan->chtype == ch_4op) { - EG_KeyOn(opl, ch * 2, egk_norm); - EG_KeyOn(opl, ch * 2 + 1, egk_norm); - EG_KeyOn(opl, (ch + 3) * 2, egk_norm); - EG_KeyOn(opl, (ch + 3) * 2 + 1, egk_norm); +void chan_writec0(opl_channel *channel, Bit8u data) { + channel->fb = (data & 0x0e) >> 1; + channel->con = data & 0x01; + channel->alg = channel->con; + if (channel->chip->newm) { + if (channel->chtype == ch_4op) { + channel->pair->alg = 0x04 | (channel->con << 1) | (channel->pair->con); + channel->alg = 0x08; + chan_setupalg(channel->pair); } - else if (chan->chtype == ch_2op || chan->chtype == ch_drum) { - EG_KeyOn(opl, ch * 2, egk_norm); - EG_KeyOn(opl, ch * 2 + 1, egk_norm); + else if (channel->chtype == ch_4op2) { + channel->alg = 0x04 | (channel->pair->con << 1) | (channel->con); + channel->pair->alg = 0x08; + chan_setupalg(channel); + } + else { + chan_setupalg(channel); } } else { - EG_KeyOn(opl, ch * 2, egk_norm); - EG_KeyOn(opl, ch * 2 + 1, egk_norm); + chan_setupalg(channel); + } + if (channel->chip->newm) { + channel->cha = ((data >> 4) & 0x01) ? ~0 : 0; + channel->chb = ((data >> 5) & 0x01) ? ~0 : 0; + } + else { + channel->cha = channel->chb = ~0; } } -void CH_Disable(chip *opl, Bit8u ch) { - channel *chan = &opl->Channels[ch]; - if (opl->newm) { - if (chan->chtype == ch_4op) { - EG_KeyOff(opl, ch * 2, egk_norm); - EG_KeyOff(opl, ch * 2 + 1, egk_norm); - EG_KeyOff(opl, (ch + 3) * 2, egk_norm); - EG_KeyOff(opl, (ch + 3) * 2 + 1, egk_norm); +void chan_generaterhythm(opl_chip *chip) { + if (chip->rhy & 0x20) { + opl_channel *channel6 = &chip->channel[6]; + opl_channel *channel7 = &chip->channel[7]; + opl_channel *channel8 = &chip->channel[8]; + slot_generate(channel6->slots[0]); + slot_generate(channel6->slots[1]); + Bit16u phase14 = channel7->slots[0]->pg_phase & 0x3ff; + Bit16u phase17 = channel8->slots[1]->pg_phase & 0x3ff; + Bit16u phase = 0x00; + //hh tc phase bit + Bit16u phasebit = ((phase14 & 0x08) | (((phase14 >> 5) ^ phase14) & 0x04) | (((phase17 >> 2) ^ phase17) & 0x08)) ? 0x01 : 0x00; + //hh + phase = (phasebit << 9) | (0x34 << ((phasebit ^ (chip->noise & 0x01) << 1))); + slot_generatephase(channel7->slots[0], phase); + //sd + phase = (0x100 << ((phase14 >> 8) & 0x01)) ^ ((chip->noise & 0x01) << 8); + slot_generatephase(channel7->slots[1], phase); + //tt + slot_generatezm(channel8->slots[0]); + //tc + phase = 0x100 | (phasebit << 9); + slot_generatephase(channel8->slots[1], phase); + } +} + +void chan_generate(opl_channel *channel) { + if (channel->chtype == ch_drum) { + return; + } + if (channel->alg & 0x08) { + return; + } + if (channel->alg & 0x04) { + slot_generate(channel->pair->slots[0]); + slot_generate(channel->pair->slots[1]); + slot_generate(channel->slots[0]); + slot_generate(channel->slots[1]); + } + else { + slot_generate(channel->slots[0]); + slot_generate(channel->slots[1]); + } +} + +void chan_enable(opl_channel *channel) { + if (channel->chip->newm) { + if (channel->chtype == ch_4op) { + eg_keyon(channel->slots[0], egk_norm); + eg_keyon(channel->slots[1], egk_norm); + eg_keyon(channel->pair->slots[0], egk_norm); + eg_keyon(channel->pair->slots[1], egk_norm); } - else if (chan->chtype == ch_2op || chan->chtype == ch_drum) { - EG_KeyOff(opl, ch * 2, egk_norm); - EG_KeyOff(opl, ch * 2 + 1, egk_norm); + else if (channel->chtype == ch_2op || channel->chtype == ch_drum) { + eg_keyon(channel->slots[0], egk_norm); + eg_keyon(channel->slots[1], egk_norm); } } else { - EG_KeyOff(opl, ch * 2, egk_norm); - EG_KeyOff(opl, ch * 2 + 1, egk_norm); + eg_keyon(channel->slots[0], egk_norm); + eg_keyon(channel->slots[1], egk_norm); + } +} + +void chan_disable(opl_channel *channel) { + if (channel->chip->newm) { + if (channel->chtype == ch_4op) { + eg_keyoff(channel->slots[0], egk_norm); + eg_keyoff(channel->slots[1], egk_norm); + eg_keyoff(channel->pair->slots[0], egk_norm); + eg_keyoff(channel->pair->slots[1], egk_norm); + } + else if (channel->chtype == ch_2op || channel->chtype == ch_drum) { + eg_keyoff(channel->slots[0], egk_norm); + eg_keyoff(channel->slots[1], egk_norm); + } + } + else { + eg_keyoff(channel->slots[0], egk_norm); + eg_keyoff(channel->slots[1], egk_norm); + } +} + +void chan_set4op(opl_chip *chip, Bit8u data) { + for (Bit8u bit = 0; bit < 6; bit++) { + Bit8u chnum = bit; + if (bit >= 3) { + chnum += 9 - 3; + } + if ((data >> bit) & 0x01) { + chip->channel[chnum].chtype = ch_4op; + chip->channel[chnum + 3].chtype = ch_4op2; + } + else { + chip->channel[chnum].chtype = ch_2op; + chip->channel[chnum + 3].chtype = ch_2op; + } } } @@ -909,99 +787,52 @@ Bit16s limshort(Bit32s a) { } void NukedOPL3::Reset() { - for (Bit8u i = 0; i < 36; i++) { - opl3.OPs[i].PG_pos = 0; - opl3.OPs[i].PG_inc = 0; - opl3.OPs[i].EG_out = 0x1ff; - opl3.OPs[i].EG_mout = 0x1ff; - opl3.OPs[i].eg_inc = 0; - opl3.OPs[i].eg_gen = 0; - opl3.OPs[i].eg_gennext = 0; - opl3.OPs[i].EG_ksl = 0; - opl3.OPs[i].EG_ar = 0; - opl3.OPs[i].EG_dr = 0; - opl3.OPs[i].EG_sl = 0; - opl3.OPs[i].EG_rr = 0; - opl3.OPs[i].EG_state = eg_off; - opl3.OPs[i].EG_type = 0; - opl3.OPs[i].out = 0; - opl3.OPs[i].prevout[0] = 0; - opl3.OPs[i].prevout[1] = 0; - opl3.OPs[i].fbmod = 0; - opl3.OPs[i].offset = op_offset[i % 18] + ((i > 17) << 8); - opl3.OPs[i].mult = 0; - opl3.OPs[i].vibrato = 0; - opl3.OPs[i].tremolo = 0; - opl3.OPs[i].ksr = 0; - opl3.OPs[i].EG_tl = 0; - opl3.OPs[i].ksl = 0; - opl3.OPs[i].key = 0; - opl3.OPs[i].waveform = 0; + memset(&opl3, 0, sizeof(opl_chip)); + for (Bit8u slotnum = 0; slotnum < 36; slotnum++) { + opl3.slot[slotnum].channel = &opl3.channel[slotnum / 2]; + opl3.slot[slotnum].chip = &opl3; + opl3.slot[slotnum].mod = &opl3.zeromod; + opl3.slot[slotnum].eg_rout = 0x1ff; + opl3.slot[slotnum].eg_out = 0x1ff; + opl3.slot[slotnum].eg_gen = envelope_gen_num_off; + opl3.slot[slotnum].eg_gennext = envelope_gen_num_off; + opl3.slot[slotnum].trem = (Bit8u*)&opl3.zeromod; } - for (Bit8u i = 0; i < 9; i++) { - opl3.Channels[i].con = 0; - opl3.Channels[i + 9].con = 0; - opl3.Channels[i].chtype = ch_2op; - opl3.Channels[i + 9].chtype = ch_2op; - opl3.Channels[i].alg = 0; - opl3.Channels[i + 9].alg = 0; - opl3.Channels[i].offset = i; - opl3.Channels[i + 9].offset = 0x100 + i; - opl3.Channels[i].feedback = 0; - opl3.Channels[i + 9].feedback = 0; - opl3.Channels[i].out = 0; - opl3.Channels[i + 9].out = 0; - opl3.Channels[i].cha = ~0; - opl3.Channels[i + 9].cha = ~0; - opl3.Channels[i].chb = ~0; - opl3.Channels[i + 9].chb = ~0; - opl3.Channels[i].chc = 0; - opl3.Channels[i + 9].chc = 0; - opl3.Channels[i].chd = 0; - opl3.Channels[i + 9].chd = 0; - opl3.Channels[i].out = 0; - opl3.Channels[i + 9].out = 0; - opl3.Channels[i].f_number = 0; - opl3.Channels[i + 9].f_number = 0; - opl3.Channels[i].block = 0; - opl3.Channels[i + 9].block = 0; - opl3.Channels[i].ksv = 0; - opl3.Channels[i + 9].ksv = 0; - opl3.Channels[i].panl = (float)CENTER_PANNING_POWER; - opl3.Channels[i + 9].panl = (float)CENTER_PANNING_POWER; - opl3.Channels[i].panr = (float)CENTER_PANNING_POWER; - opl3.Channels[i + 9].panr = (float)CENTER_PANNING_POWER; + for (Bit8u channum = 0; channum < 18; channum++) { + opl3.channel[channum].slots[0] = &opl3.slot[2 * channum]; + opl3.channel[channum].slots[1] = &opl3.slot[2 * channum + 1]; + if ((channum % 9) < 3) { + opl3.channel[channum].pair = &opl3.channel[channum + 3]; + } + else if ((channum % 9) < 6) { + opl3.channel[channum].pair = &opl3.channel[channum - 3]; + } + opl3.channel[channum].chip = &opl3; + opl3.channel[channum].out[0] = &opl3.zeromod; + opl3.channel[channum].out[1] = &opl3.zeromod; + opl3.channel[channum].out[2] = &opl3.zeromod; + opl3.channel[channum].out[3] = &opl3.zeromod; + opl3.channel[channum].chtype = ch_2op; + opl3.channel[channum].cha = ~0; + opl3.channel[channum].chb = ~0; + opl3.channel[channum].fcha = 1.0; + opl3.channel[channum].fchb = 1.0; + chan_setupalg(&opl3.channel[channum]); } - memset(opl3.opl_memory, 0, 0x200); - opl3.newm = 0; - opl3.nts = 0; - opl3.rhythm = 0; - opl3.dvb = 0; - opl3.dam = 0; opl3.noise = 0x306600; - opl3.vib_pos = 0; - opl3.timer = 0; - opl3.trem_inc = 0; - opl3.trem_tval = 0; - opl3.trem_dir = 0; - opl3.trem_val = 0; - opl3.zm = 0; - CH_Set2OP(&opl3); } void NukedOPL3::WriteReg(int reg, int v) { v &= 0xff; reg &= 0x1ff; - Bit8u highbank = (reg >> 8) & 0x01; + Bit8u high = (reg >> 8) & 0x01; Bit8u regm = reg & 0xff; - opl3.opl_memory[reg & 0x1ff] = v; switch (regm & 0xf0) { case 0x00: - if (highbank) { + if (high) { switch (regm & 0x0f) { case 0x04: - CH_Set2OP(&opl3); - CH_Set4OP(&opl3); + chan_set4op(&opl3, v); break; case 0x05: opl3.newm = v & 0x01; @@ -1019,57 +850,57 @@ void NukedOPL3::WriteReg(int reg, int v) { case 0x20: case 0x30: if (ad_slot[regm & 0x1f] >= 0) { - OP_Update20(&opl3, 18 * highbank + ad_slot[regm & 0x1f]); + slot_write20(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v); } break; case 0x40: case 0x50: if (ad_slot[regm & 0x1f] >= 0) { - OP_Update40(&opl3, 18 * highbank + ad_slot[regm & 0x1f]); + slot_write40(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v); } break; case 0x60: case 0x70: if (ad_slot[regm & 0x1f] >= 0) { - OP_Update60(&opl3, 18 * highbank + ad_slot[regm & 0x1f]); + slot_write60(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v); } break; case 0x80: case 0x90: if (ad_slot[regm & 0x1f] >= 0) { - OP_Update80(&opl3, 18 * highbank + ad_slot[regm & 0x1f]); + slot_write80(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v);; } break; case 0xe0: case 0xf0: if (ad_slot[regm & 0x1f] >= 0) { - OP_UpdateE0(&opl3, 18 * highbank + ad_slot[regm & 0x1f]); + slot_writee0(&opl3.slot[18 * high + ad_slot[regm & 0x1f]], v); } break; case 0xa0: if ((regm & 0x0f) < 9) { - CH_UpdateAB0(&opl3, 9 * highbank + (regm & 0x0f)); + chan_writea0(&opl3.channel[9 * high + (regm & 0x0f)], v); } break; case 0xb0: - if (regm == 0xbd && !highbank) { + if (regm == 0xbd && !high) { opl3.dam = v >> 7; opl3.dvb = (v >> 6) & 0x01; - CH_UpdateRhythm(&opl3); + chan_updaterhythm(&opl3, v); } else if ((regm & 0x0f) < 9) { - CH_UpdateAB0(&opl3, 9 * highbank + (regm & 0x0f)); + chan_writeb0(&opl3.channel[9 * high + (regm & 0x0f)], v); if (v & 0x20) { - CH_Enable(&opl3, 9 * highbank + (regm & 0x0f)); + chan_enable(&opl3.channel[9 * high + (regm & 0x0f)]); } else { - CH_Disable(&opl3, 9 * highbank + (regm & 0x0f)); + chan_disable(&opl3.channel[9 * high + (regm & 0x0f)]); } } break; case 0xc0: if ((regm & 0x0f) < 9) { - CH_UpdateC0(&opl3, 9 * highbank + (regm & 0x0f)); + chan_writec0(&opl3.channel[9 * high + (regm & 0x0f)], v); } break; } @@ -1077,54 +908,55 @@ void NukedOPL3::WriteReg(int reg, int v) { void NukedOPL3::Update(float* sndptr, int numsamples) { Bit32s outa, outb; - Bit8u ii = 0; for (Bit32u i = 0; i < (Bit32u)numsamples; i++) { outa = 0; outb = 0; - for (ii = 0; ii < 36; ii++) { - OP_CalcFB(&opl3, ii); + for (Bit8u ii = 0; ii < 36; ii++) { + slot_calgfb(&opl3.slot[ii]); } - CH_GenerateRhythm(&opl3); - for (ii = 0; ii < 18; ii++) { - CH_Generate(&opl3, ii); + chan_generaterhythm(&opl3); + for (Bit8u ii = 0; ii < 18; ii++) { + chan_generate(&opl3.channel[ii]); + Bit16s accm = 0; + for (Bit8u jj = 0; jj < 4; jj++) { + accm += *opl3.channel[ii].out[jj]; + } if (FullPan) { - outa += (Bit16s)(opl3.Channels[ii].out * opl3.Channels[ii].panl); - outb += (Bit16s)(opl3.Channels[ii].out * opl3.Channels[ii].panr); + outa += (Bit16s)(accm * opl3.channel[ii].fcha); + outb += (Bit16s)(accm * opl3.channel[ii].fchb); } else { - outa += (Bit16s)(opl3.Channels[ii].out & opl3.Channels[ii].cha); - outb += (Bit16s)(opl3.Channels[ii].out & opl3.Channels[ii].chb); + outa += (Bit16s)(accm & opl3.channel[ii].cha); + outb += (Bit16s)(accm & opl3.channel[ii].chb); } } - for (ii = 0; ii < 36; ii++) { - EG_Generate(&opl3, ii); - PG_Generate(&opl3, ii); - } - N_Generate(&opl3); - opl3.trem_inc++; - if (!(opl3.trem_inc & 0x3f)) { - if (!opl3.trem_dir) { - if (opl3.trem_tval == 105) { - opl3.trem_tval--; - opl3.trem_dir = 1; - } - else { - opl3.trem_tval++; - } - } - else { - if (opl3.trem_tval == 0) { - opl3.trem_tval++; - opl3.trem_dir = 0; - } - else { - opl3.trem_tval--; - } - } - opl3.trem_val = (opl3.trem_tval >> 2) >> ((!opl3.dam) << 1); + for (Bit8u ii = 0; ii < 36; ii++) { + envelope_calc(&opl3.slot[ii]); + pg_generate(&opl3.slot[ii]); } + n_generate(&opl3); opl3.timer++; - opl3.vib_pos = (opl3.timer >> 10) & 0x07; + if (!(opl3.timer & 0x3f)) { + if (!opl3.tremdir) { + if (opl3.tremtval == 105) { + opl3.tremtval--; + opl3.tremdir = 1; + } + else { + opl3.tremtval++; + } + } + else { + if (opl3.tremtval == 0) { + opl3.tremtval++; + opl3.tremdir = 0; + } + else { + opl3.tremtval--; + } + } + opl3.tremval = (opl3.tremtval >> 2) >> ((1 - opl3.dam) << 1); + } *sndptr++ += (float)(outa / 10240.0); *sndptr++ += (float)(outb / 10240.0); } @@ -1132,8 +964,8 @@ void NukedOPL3::Update(float* sndptr, int numsamples) { void NukedOPL3::SetPanning(int c, float left, float right) { if (FullPan) { - opl3.Channels[c].panl = left; - opl3.Channels[c].panr = right; + opl3.channel[c].fcha = left; + opl3.channel[c].fchb = right; } } @@ -1144,4 +976,4 @@ NukedOPL3::NukedOPL3(bool stereo) { OPLEmul *NukedOPL3Create(bool stereo) { return new NukedOPL3(stereo); -} +} \ No newline at end of file diff --git a/src/oplsynth/nukedopl3.h b/src/oplsynth/nukedopl3.h index 15f264c1a..ccf37fe14 100644 --- a/src/oplsynth/nukedopl3.h +++ b/src/oplsynth/nukedopl3.h @@ -5,12 +5,12 @@ * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. -* +* * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. -* +* * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA @@ -23,11 +23,11 @@ Feedback and Rhythm part calculation information. forums.submarine.org.uk(carbon14, opl3): Tremolo and phase generator calculation information. - OPLx decapsulated(Matthew Gambrell and Olli Niemitalo): + OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): OPL2 ROMs. */ -//version 1.4.2 +//version 1.5 #include "opl.h" #include "muslib.h" @@ -41,74 +41,188 @@ typedef SWORD Bit16s; typedef BYTE Bit8u; typedef SBYTE Bit8s; -struct channel { - Bit8u con; - Bit8u chtype; - Bit8u alg; - Bit16u offset; - Bit8u feedback; - Bit16u cha, chb, chc, chd; - Bit16s out; - Bit16u f_number; - Bit8u block; - Bit8u ksv; - float panl; - float panr; +// Channel types + +enum { + ch_2op = 0, + ch_4op = 1, + ch_4op2 = 2, + ch_drum = 3 }; -struct slot { - Bit32u PG_pos; - Bit32u PG_inc; - Bit16s EG_out; +// Envelope key types + +enum { + egk_norm = 0x01, + egk_drum = 0x02 +}; + + +// +// logsin table +// + +static const Bit16u logsinrom[256] = { + 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, + 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, + 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, + 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, + 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, + 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, + 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, + 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, + 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, + 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, + 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, + 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, + 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, + 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, + 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, + 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 +}; + +// +// exp table +// + +static const Bit16u exprom[256] = { + 0x000, 0x003, 0x006, 0x008, 0x00b, 0x00e, 0x011, 0x014, 0x016, 0x019, 0x01c, 0x01f, 0x022, 0x025, 0x028, 0x02a, + 0x02d, 0x030, 0x033, 0x036, 0x039, 0x03c, 0x03f, 0x042, 0x045, 0x048, 0x04b, 0x04e, 0x051, 0x054, 0x057, 0x05a, + 0x05d, 0x060, 0x063, 0x066, 0x069, 0x06c, 0x06f, 0x072, 0x075, 0x078, 0x07b, 0x07e, 0x082, 0x085, 0x088, 0x08b, + 0x08e, 0x091, 0x094, 0x098, 0x09b, 0x09e, 0x0a1, 0x0a4, 0x0a8, 0x0ab, 0x0ae, 0x0b1, 0x0b5, 0x0b8, 0x0bb, 0x0be, + 0x0c2, 0x0c5, 0x0c8, 0x0cc, 0x0cf, 0x0d2, 0x0d6, 0x0d9, 0x0dc, 0x0e0, 0x0e3, 0x0e7, 0x0ea, 0x0ed, 0x0f1, 0x0f4, + 0x0f8, 0x0fb, 0x0ff, 0x102, 0x106, 0x109, 0x10c, 0x110, 0x114, 0x117, 0x11b, 0x11e, 0x122, 0x125, 0x129, 0x12c, + 0x130, 0x134, 0x137, 0x13b, 0x13e, 0x142, 0x146, 0x149, 0x14d, 0x151, 0x154, 0x158, 0x15c, 0x160, 0x163, 0x167, + 0x16b, 0x16f, 0x172, 0x176, 0x17a, 0x17e, 0x181, 0x185, 0x189, 0x18d, 0x191, 0x195, 0x199, 0x19c, 0x1a0, 0x1a4, + 0x1a8, 0x1ac, 0x1b0, 0x1b4, 0x1b8, 0x1bc, 0x1c0, 0x1c4, 0x1c8, 0x1cc, 0x1d0, 0x1d4, 0x1d8, 0x1dc, 0x1e0, 0x1e4, + 0x1e8, 0x1ec, 0x1f0, 0x1f5, 0x1f9, 0x1fd, 0x201, 0x205, 0x209, 0x20e, 0x212, 0x216, 0x21a, 0x21e, 0x223, 0x227, + 0x22b, 0x230, 0x234, 0x238, 0x23c, 0x241, 0x245, 0x249, 0x24e, 0x252, 0x257, 0x25b, 0x25f, 0x264, 0x268, 0x26d, + 0x271, 0x276, 0x27a, 0x27f, 0x283, 0x288, 0x28c, 0x291, 0x295, 0x29a, 0x29e, 0x2a3, 0x2a8, 0x2ac, 0x2b1, 0x2b5, + 0x2ba, 0x2bf, 0x2c4, 0x2c8, 0x2cd, 0x2d2, 0x2d6, 0x2db, 0x2e0, 0x2e5, 0x2e9, 0x2ee, 0x2f3, 0x2f8, 0x2fd, 0x302, + 0x306, 0x30b, 0x310, 0x315, 0x31a, 0x31f, 0x324, 0x329, 0x32e, 0x333, 0x338, 0x33d, 0x342, 0x347, 0x34c, 0x351, + 0x356, 0x35b, 0x360, 0x365, 0x36a, 0x370, 0x375, 0x37a, 0x37f, 0x384, 0x38a, 0x38f, 0x394, 0x399, 0x39f, 0x3a4, + 0x3a9, 0x3ae, 0x3b4, 0x3b9, 0x3bf, 0x3c4, 0x3c9, 0x3cf, 0x3d4, 0x3da, 0x3df, 0x3e4, 0x3ea, 0x3ef, 0x3f5, 0x3fa +}; + +// +// freq mult table multiplied by 2 +// +// 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 12, 12, 15, 15 +// + +static const Bit8u mt[16] = { 1, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 20, 24, 24, 30, 30 }; + +// +// ksl table +// + +static const Bit8u kslrom[16] = { 0, 64, 80, 90, 96, 102, 106, 110, 112, 116, 118, 120, 122, 124, 126, 127 }; + +static const Bit8u kslshift[4] = { 8, 1, 2, 0 }; + +// +// LFO vibrato +// + +static const Bit8u vib_table[8] = { 3, 1, 0, 1, 3, 1, 0, 1 }; +static const Bit8s vibsgn_table[8] = { 1, 1, 1, 1, -1, -1, -1, -1 }; + +// +// envelope generator constants +// + +static const Bit8u eg_incstep[3][4][8] = { + { { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0 } }, + { { 0, 1, 0, 1, 0, 1, 0, 1 }, { 1, 1, 0, 1, 0, 1, 0, 1 }, { 1, 1, 0, 1, 1, 1, 0, 1 }, { 1, 1, 1, 1, 1, 1, 0, 1 } }, + { { 1, 1, 1, 1, 1, 1, 1, 1 }, { 2, 2, 1, 1, 1, 1, 1, 1 }, { 2, 2, 1, 1, 2, 2, 1, 1 }, { 2, 2, 2, 2, 2, 2, 1, 1 } } +}; + +static const Bit8u eg_incdesc[16] = { + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2 +}; + +static const Bit8s eg_incsh[16] = { + 0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, -1, -2 +}; + +// +// address decoding +// + +static const Bit8s ad_slot[0x20] = { 0, 2, 4, 1, 3, 5, -1, -1, 6, 8, 10, 7, 9, 11, -1, -1, 12, 14, 16, 13, 15, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + + +struct opl_chip; +struct opl_slot; +struct opl_channel; + +struct opl_slot { + opl_channel *channel; + opl_chip *chip; + Bit16s out; + Bit16s fbmod; + Bit16s *mod; + Bit16s prout[2]; + Bit16s eg_rout; + Bit16s eg_out; Bit8u eg_inc; Bit8u eg_gen; Bit8u eg_gennext; - Bit16u EG_mout; - Bit8u EG_ksl; - Bit8u EG_ar; - Bit8u EG_dr; - Bit8u EG_sl; - Bit8u EG_rr; - Bit8u EG_state; - Bit8u EG_type; - Bit16s out; - Bit16s *mod; - Bit16s prevout[2]; - Bit16s fbmod; - Bit16u offset; - Bit8u mult; - Bit8u vibrato; - Bit8u tremolo; - Bit8u ksr; - Bit8u EG_tl; - Bit8u ksl; + Bit8u eg_rate; + Bit8u eg_ksl; + Bit8u *trem; + Bit8u reg_vib; + Bit8u reg_type; + Bit8u reg_ksr; + Bit8u reg_mult; + Bit8u reg_ksl; + Bit8u reg_tl; + Bit8u reg_ar; + Bit8u reg_dr; + Bit8u reg_sl; + Bit8u reg_rr; + Bit8u reg_wf; Bit8u key; - Bit8u waveform; + Bit32u pg_phase; }; +struct opl_channel { + opl_slot *slots[2]; + opl_channel *pair; + opl_chip *chip; + Bit16s *out[4]; + Bit8u chtype; + Bit16u f_num; + Bit8u block; + Bit8u fb; + Bit8u con; + Bit8u alg; + Bit8u ksv; + Bit16u cha, chb; + float fcha, fchb; +}; -struct chip { - Bit8u opl_memory[0x200]; +struct opl_chip { + opl_channel channel[18]; + opl_slot slot[36]; + Bit16u timer; Bit8u newm; Bit8u nts; - Bit8u rhythm; Bit8u dvb; Bit8u dam; + Bit8u rhy; + Bit8u vibpos; + Bit8u tremval; + Bit8u tremtval; + Bit8u tremdir; Bit32u noise; - Bit16u vib_pos; - Bit16u timer; - Bit8u trem_inc; - Bit8u trem_tval; - Bit8u trem_dir; - Bit8u trem_val; - channel Channels[18]; - slot OPs[36]; - Bit16s zm; + Bit16s zeromod; }; + class NukedOPL3 : public OPLEmul { private: - chip opl3; + opl_chip opl3; bool FullPan; public: void Reset(); diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 2ff70c9e3..866087688 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -7725,13 +7725,6 @@ scriptwait: AddToConsole (-1, consolecolor); AddToConsole (-1, work); AddToConsole (-1, bar); - if (Logfile) - { - fputs (logbar, Logfile); - fputs (work, Logfile); - fputs (logbar, Logfile); - fflush (Logfile); - } } } } diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 0d74b4d42..67215cf41 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -1510,7 +1510,7 @@ FUNC(LS_Thing_Raise) if (arg0==0) { - ok = P_Thing_Raise (it); + ok = P_Thing_Raise (it,NULL); } else { @@ -1518,7 +1518,7 @@ FUNC(LS_Thing_Raise) while ( (target = iterator.Next ()) ) { - ok |= P_Thing_Raise(target); + ok |= P_Thing_Raise(target,NULL); } } return ok; diff --git a/src/p_local.h b/src/p_local.h index dec80ecaa..c0a754e25 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -171,7 +171,7 @@ bool P_Thing_Move (int tid, AActor *source, int mapspot, bool fog); int P_Thing_Damage (int tid, AActor *whofor0, int amount, FName type); void P_Thing_SetVelocity(AActor *actor, fixed_t vx, fixed_t vy, fixed_t vz, bool add, bool setbob); void P_RemoveThing(AActor * actor); -bool P_Thing_Raise(AActor *thing); +bool P_Thing_Raise(AActor *thing, AActor *raiser); bool P_Thing_CanRaise(AActor *thing); const PClass *P_GetSpawnableType(int spawnnum); diff --git a/src/p_map.cpp b/src/p_map.cpp index c0e5c3baa..857597089 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -74,6 +74,69 @@ TArray spechit; // Temporary holder for thing_sectorlist threads msecnode_t* sector_list = NULL; // phares 3/16/98 +//========================================================================== +// +// GetCoefficientClosestPointInLine24 +// +// Formula: (dotProduct(ldv1 - tm, ld) << 24) / dotProduct(ld, ld) +// with: ldv1 = (ld->v1->x, ld->v1->y), tm = (tm.x, tm.y) +// and ld = (ld->dx, ld->dy) +// Returns truncated to range [0, 1 << 24]. +// +//========================================================================== + +static fixed_t GetCoefficientClosestPointInLine24(line_t *ld, FCheckPosition &tm) +{ + // [EP] Use 64 bit integers in order to keep the exact result of the + // multiplication, because in the case the vertexes have both the + // distance coordinates equal to the map limit (32767 units, which is + // 2147418112 in fixed_t notation), the product result would occupy + // 62 bits and the sum of two products would occupy 63 bits + // in the worst case. If instead the vertexes are very close (1 in + // fixed_t notation, which is 1.52587890625e-05 in float notation), the + // product and the sum can be 1 in the worst case, which is very tiny. + + SQWORD r_num = ((SQWORD(tm.x - ld->v1->x)*ld->dx) + + (SQWORD(tm.y - ld->v1->y)*ld->dy)); + + // The denominator is always positive. Use this to avoid useless + // calculations. + SQWORD r_den = (SQWORD(ld->dx)*ld->dx + SQWORD(ld->dy)*ld->dy); + + if (r_num <= 0) { + // [EP] The numerator is less or equal to zero, hence the closest + // point on the line is the first vertex. Truncate the result to 0. + return 0; + } + + if (r_num >= r_den) { + // [EP] The division is greater or equal to 1, hence the closest + // point on the line is the second vertex. Truncate the result to + // 1 << 24. + return (1 << 24); + } + + // [EP] Deal with the limited bits. The original formula is: + // r = (r_num << 24) / r_den, + // but r_num might be big enough to make the shift overflow. + // Since the numerator can't be saved in a 128bit integer, + // the denominator must be right shifted. If the denominator is + // less than (1 << 24), there would be a division by zero. + // Thanks to the fact that in this code path the denominator is greater + // than the numerator, it's possible to avoid this bad situation by + // just checking the last 24 bits of the numerator. + if ((r_num >> (63-24)) != 0) { + // [EP] In fact, if the numerator is greater than + // (1 << (63-24)), the denominator must be greater than + // (1 << (63-24)), hence the denominator won't be zero after + // the right shift by 24 places. + return (fixed_t)(r_num/(r_den >> 24)); + } + // [EP] Having the last 24 bits all zero allows left shifting + // the numerator by 24 bits without overflow. + return (fixed_t)((r_num << 24)/r_den); +} + //========================================================================== // // PIT_FindFloorCeiling @@ -736,19 +799,8 @@ bool PIT_CheckLine(line_t *ld, const FBoundingBox &box, FCheckPosition &tm) else { // Find the point on the line closest to the actor's center, and use // that to calculate openings - // [EP] Use 64 bit integers in order to keep the exact result of the - // multiplication, because in the worst case, which is by the map limit - // (32767 units, which is 2147418112 in fixed_t notation), the result - // would occupy 62 bits (if I consider also the addition with another - // and possible 62 bit value, it's 63 bits). - // This privilege could not be available if the starting data would be - // 64 bit long. - // With this, the division is exact as the 32 bit float counterpart, - // though I don't know why I had to discard the first 24 bits from the - // divisor. - SQWORD r_num = ((SQWORD(tm.x - ld->v1->x)*ld->dx) + (SQWORD(tm.y - ld->v1->y)*ld->dy)); - SQWORD r_den = (SQWORD(ld->dx)*ld->dx + SQWORD(ld->dy)*ld->dy) / (1 << 24); - fixed_t r = (fixed_t)(r_num / r_den); + fixed_t r = GetCoefficientClosestPointInLine24(ld, tm); + /* Printf ("%d:%d: %d (%d %d %d %d) (%d %d %d %d)\n", level.time, ld-lines, r, ld->frontsector->floorplane.a, ld->frontsector->floorplane.b, @@ -1231,6 +1283,16 @@ bool PIT_CheckThing(AActor *thing, FCheckPosition &tm) { P_GiveBody(thing, -damage); } + + if ((thing->flags7 & MF7_THRUREFLECT) && (thing->flags2 & MF2_REFLECTIVE) && (tm.thing->flags & MF_MISSILE)) + { + if (tm.thing->flags2 & MF2_SEEKERMISSILE) + { + tm.thing->tracer = tm.thing->target; + } + tm.thing->target = thing; + return true; + } return false; // don't traverse any more } if (thing->flags2 & MF2_PUSHABLE && !(tm.thing->flags2 & MF2_CANNOTPUSH)) @@ -1591,7 +1653,7 @@ bool P_TestMobjZ(AActor *actor, bool quick, AActor **pOnmobj) { // Don't clip against self continue; } - if ((actor->flags & MF_MISSILE) && thing == actor->target) + if ((actor->flags & MF_MISSILE) && (thing == actor->target)) { // Don't clip against whoever shot the missile. continue; } @@ -2931,18 +2993,20 @@ bool P_BounceWall(AActor *mo) extern FRandom pr_bounce; bool P_BounceActor(AActor *mo, AActor *BlockingMobj, bool ontop) { + //Don't go through all of this if the actor is reflective and wants things to pass through them. + if (BlockingMobj && ((BlockingMobj->flags2 & MF2_REFLECTIVE) && (BlockingMobj->flags7 & MF7_THRUREFLECT))) return true; if (mo && BlockingMobj && ((mo->BounceFlags & BOUNCE_AllActors) - || ((mo->flags & MF_MISSILE) && (!(mo->flags2 & MF2_RIP) || (BlockingMobj->flags5 & MF5_DONTRIP) || ((mo->flags6 & MF6_NOBOSSRIP) && (BlockingMobj->flags2 & MF2_BOSS))) && (BlockingMobj->flags2 & MF2_REFLECTIVE)) - || ((BlockingMobj->player == NULL) && (!(BlockingMobj->flags3 & MF3_ISMONSTER))) - )) + || ((mo->flags & MF_MISSILE) && (!(mo->flags2 & MF2_RIP) + || (BlockingMobj->flags5 & MF5_DONTRIP) + || ((mo->flags6 & MF6_NOBOSSRIP) && (BlockingMobj->flags2 & MF2_BOSS))) && (BlockingMobj->flags2 & MF2_REFLECTIVE)) + || ((BlockingMobj->player == NULL) && (!(BlockingMobj->flags3 & MF3_ISMONSTER))))) { if (mo->bouncecount > 0 && --mo->bouncecount == 0) return false; if (!ontop) { fixed_t speed; - angle_t angle = R_PointToAngle2(BlockingMobj->x, - BlockingMobj->y, mo->x, mo->y) + ANGLE_1*((pr_bounce() % 16) - 8); + angle_t angle = R_PointToAngle2(BlockingMobj->x,BlockingMobj->y, mo->x, mo->y) + ANGLE_1*((pr_bounce() % 16) - 8); speed = P_AproxDistance(mo->velx, mo->vely); speed = FixedMul(speed, mo->wallbouncefactor); // [GZ] was 0.75, using wallbouncefactor seems more consistent mo->angle = angle; @@ -5038,6 +5102,8 @@ int P_PushUp(AActor *thing, FChangePosition *cpos) // is normally for projectiles which would have exploded by now anyway... if (thing->flags6 & MF6_THRUSPECIES && thing->GetSpecies() == intersect->GetSpecies()) continue; + if ((thing->flags & MF_MISSILE) && (intersect->flags2 & MF2_REFLECTIVE) && (intersect->flags7 & MF7_THRUREFLECT)) + continue; if (!(intersect->flags2 & MF2_PASSMOBJ) || (!(intersect->flags3 & MF3_ISMONSTER) && intersect->Mass > mymass) || (intersect->flags4 & MF4_ACTLIKEBRIDGE) @@ -5046,7 +5112,8 @@ int P_PushUp(AActor *thing, FChangePosition *cpos) // Can't push bridges or things more massive than ourself return 2; } - fixed_t oldz = intersect->z; + fixed_t oldz; + oldz = intersect->z; P_AdjustFloorCeil(intersect, cpos); intersect->z = thing->z + thing->height + 1; if (P_PushUp(intersect, cpos)) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index cf6bccc8a..49b4b983f 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1202,6 +1202,9 @@ void P_ExplodeMissile (AActor *mo, line_t *line, AActor *target) if (target != NULL && ((target->flags & (MF_SHOOTABLE|MF_CORPSE)) || (target->flags6 & MF6_KILLED)) ) { + if (mo->flags7 & MF7_HITTARGET) mo->target = target; + if (mo->flags7 & MF7_HITMASTER) mo->master = target; + if (mo->flags7 & MF7_HITTRACER) mo->tracer = target; if (target->flags & MF_NOBLOOD) nextstate = mo->FindState(NAME_Crash); if (nextstate == NULL) nextstate = mo->FindState(NAME_Death, NAME_Extreme); } @@ -1660,6 +1663,7 @@ fixed_t P_XYMovement (AActor *mo, fixed_t scrollx, fixed_t scrolly) int steps, step, totalsteps; fixed_t startx, starty; fixed_t oldfloorz = mo->floorz; + fixed_t oldz = mo->z; fixed_t maxmove = (mo->waterlevel < 1) || (mo->flags & MF_MISSILE) || (mo->player && mo->player->crouchoffset<-10*FRACUNIT) ? MAXMOVE : MAXMOVE/4; @@ -1949,20 +1953,53 @@ fixed_t P_XYMovement (AActor *mo, fixed_t scrollx, fixed_t scrolly) } if (BlockingMobj && (BlockingMobj->flags2 & MF2_REFLECTIVE)) { - angle = R_PointToAngle2(BlockingMobj->x, BlockingMobj->y, mo->x, mo->y); - - // Change angle for deflection/reflection - if (mo->AdjustReflectionAngle (BlockingMobj, angle)) + bool seeker = (mo->flags2 & MF2_SEEKERMISSILE) ? true : false; + // Don't change the angle if there's THRUREFLECT on the monster. + if (!(BlockingMobj->flags7 & MF7_THRUREFLECT)) { - goto explode; - } + int dir; + angle_t delta; + + if (BlockingMobj->flags7 & MF7_MIRRORREFLECT) + angle = mo->angle + ANG180; + else + angle = R_PointToAngle2(BlockingMobj->x, BlockingMobj->y, mo->x, mo->y); - // Reflect the missile along angle - mo->angle = angle; - angle >>= ANGLETOFINESHIFT; - mo->velx = FixedMul (mo->Speed>>1, finecosine[angle]); - mo->vely = FixedMul (mo->Speed>>1, finesine[angle]); - mo->velz = -mo->velz/2; + // Change angle for deflection/reflection + // AIMREFLECT calls precedence so make sure not to bother with adjusting here if declared. + if (!(BlockingMobj->flags7 & MF7_AIMREFLECT) && (mo->AdjustReflectionAngle(BlockingMobj, angle))) + { + goto explode; + } + + // Reflect the missile along angle + if (BlockingMobj->flags7 & MF7_AIMREFLECT) + { + dir = P_FaceMobj(mo, mo->target, &delta); + if (dir) + { // Turn clockwise + mo->angle += delta; + } + else + { // Turn counter clockwise + mo->angle -= delta; + } + angle = mo->angle >> ANGLETOFINESHIFT; + mo->velx = FixedMul(mo->Speed, finecosine[angle]); + mo->vely = FixedMul(mo->Speed, finesine[angle]); + mo->velz = -mo->velz; + } + else + { + mo->angle = angle; + angle >>= ANGLETOFINESHIFT; + mo->velx = FixedMul(mo->Speed >> 1, finecosine[angle]); + mo->vely = FixedMul(mo->Speed >> 1, finesine[angle]); + mo->velz = -mo->velz / 2; + } + + + } if (mo->flags2 & MF2_SEEKERMISSILE) { mo->tracer = mo->target; @@ -2893,6 +2930,7 @@ int AActor::SpecialMissileHit (AActor *victim) bool AActor::AdjustReflectionAngle (AActor *thing, angle_t &angle) { if (flags2 & MF2_DONTREFLECT) return true; + if (thing->flags7 & MF7_THRUREFLECT) return false; // Change angle for reflection if (thing->flags4&MF4_SHIELDREFLECT) @@ -3259,7 +3297,7 @@ void AActor::Tick () else if (flags & MF_SPECIAL) { //Item pickup time //clock (BotWTG); - bglobal.WhatToGet (players[i].mo, this); + players[i].Bot->WhatToGet (this); //unclock (BotWTG); BotWTG++; } @@ -3267,7 +3305,7 @@ void AActor::Tick () { if (!players[i].Bot->missile && (flags3 & MF3_WARNBOT)) { //warn for incoming missiles. - if (target != players[i].mo && bglobal.Check_LOS (players[i].mo, this, ANGLE_90)) + if (target != players[i].mo && players[i].Bot->Check_LOS (this, ANGLE_90)) players[i].Bot->missile = this; } } @@ -4302,12 +4340,15 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags) { spawn_x = p->mo->x; spawn_y = p->mo->y; + spawn_z = p->mo->z; + spawn_angle = p->mo->angle; } else { spawn_x = mthing->x; spawn_y = mthing->y; + // Allow full angular precision but avoid roundoff errors for multiples of 45 degrees. if (mthing->angle % 45 != 0) { @@ -4321,14 +4362,14 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags) { spawn_angle += 1 << ANGLETOFINESHIFT; } - } - if (GetDefaultByType(p->cls)->flags & MF_SPAWNCEILING) - spawn_z = ONCEILINGZ; - else if (GetDefaultByType(p->cls)->flags2 & MF2_SPAWNFLOAT) - spawn_z = FLOATRANDZ; - else - spawn_z = ONFLOORZ; + if (GetDefaultByType(p->cls)->flags & MF_SPAWNCEILING) + spawn_z = ONCEILINGZ; + else if (GetDefaultByType(p->cls)->flags2 & MF2_SPAWNFLOAT) + spawn_z = FLOATRANDZ; + else + spawn_z = ONFLOORZ; + } mobj = static_cast (Spawn (p->cls, spawn_x, spawn_y, spawn_z, NO_REPLACE)); @@ -4445,7 +4486,8 @@ APlayerPawn *P_SpawnPlayer (FPlayerStart *mthing, int playernum, int flags) { APowerup *invul = static_cast(p->mo->GiveInventoryType (RUNTIME_CLASS(APowerInvulnerable))); invul->EffectTics = 3*TICRATE; - invul->BlendColor = 0; // don't mess with the view + invul->BlendColor = 0; // don't mess with the view + invul->ItemFlags |= IF_UNDROPPABLE; // Don't drop this p->mo->effects |= FX_RESPAWNINVUL; // [RH] special effect } diff --git a/src/p_things.cpp b/src/p_things.cpp index 0189848e4..63173564c 100644 --- a/src/p_things.cpp +++ b/src/p_things.cpp @@ -412,7 +412,7 @@ void P_RemoveThing(AActor * actor) } -bool P_Thing_Raise(AActor *thing) +bool P_Thing_Raise(AActor *thing, AActor *raiser) { FState * RaiseState = thing->GetRaiseState(); if (RaiseState == NULL) @@ -445,6 +445,12 @@ bool P_Thing_Raise(AActor *thing) thing->Revive(); + if (raiser != NULL) + { + // Let's copy the friendliness of the one who raised it. + thing->CopyFriendliness(raiser, false); + } + thing->SetState (RaiseState); return true; } diff --git a/src/r_things.cpp b/src/r_things.cpp index c0475ee75..e27a5c87e 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -95,6 +95,7 @@ extern fixed_t globaluclip, globaldclip; EXTERN_CVAR (Bool, st_scale) EXTERN_CVAR(Bool, r_shadercolormaps) EXTERN_CVAR(Int, r_drawfuzz) +EXTERN_CVAR(Bool, r_deathcamera); // // Sprite rotation 0 is facing the viewer, @@ -1410,7 +1411,8 @@ void R_DrawPlayerSprites () if (!r_drawplayersprites || !camera->player || - (players[consoleplayer].cheats & CF_CHASECAM)) + (players[consoleplayer].cheats & CF_CHASECAM) || + (r_deathcamera && camera->health <= 0)) return; if(fixedlightlev < 0 && viewsector->e && viewsector->e->XFloor.lightlist.Size()) { diff --git a/src/sc_man_scanner.re b/src/sc_man_scanner.re index dd90ce1d1..3a7f717a8 100644 --- a/src/sc_man_scanner.re +++ b/src/sc_man_scanner.re @@ -158,6 +158,7 @@ std2: 'random' { RET(TK_Random); } 'random2' { RET(TK_Random2); } 'frandom' { RET(TK_FRandom); } + 'pick' { RET(TK_Pick); } L (L|D)* { RET(TK_Identifier); } diff --git a/src/sc_man_tokens.h b/src/sc_man_tokens.h index 9dde74972..1c22046c9 100644 --- a/src/sc_man_tokens.h +++ b/src/sc_man_tokens.h @@ -122,4 +122,5 @@ xx(TK_Array, "'array'") xx(TK_In, "'in'") xx(TK_SizeOf, "'sizeof'") xx(TK_AlignOf, "'alignof'") +xx(TK_Pick, "'pick'") #undef xx diff --git a/src/sdl/i_steam.cpp b/src/sdl/i_steam.cpp new file mode 100644 index 000000000..23e74af5a --- /dev/null +++ b/src/sdl/i_steam.cpp @@ -0,0 +1,217 @@ +/* +** i_steam.cpp +** +**--------------------------------------------------------------------------- +** Copyright 2013 Braden Obrzut +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +** +*/ + +#include + +#include "doomerrors.h" +#include "d_main.h" +#include "zstring.h" +#include "sc_man.h" + +static void PSR_FindEndBlock(FScanner &sc) +{ + int depth = 1; + do + { + if(sc.CheckToken('}')) + --depth; + else if(sc.CheckToken('{')) + ++depth; + else + sc.MustGetAnyToken(); + } + while(depth); +} +static void PSR_SkipBlock(FScanner &sc) +{ + sc.MustGetToken('{'); + PSR_FindEndBlock(sc); +} +static bool PSR_FindAndEnterBlock(FScanner &sc, const char* keyword) +{ + // Finds a block with a given keyword and then enter it (opening brace) + // Should be closed with PSR_FindEndBlock + while(sc.GetToken()) + { + if(sc.TokenType == '}') + { + sc.UnGet(); + return false; + } + + sc.TokenMustBe(TK_StringConst); + if(!sc.Compare(keyword)) + { + if(!sc.CheckToken(TK_StringConst)) + PSR_SkipBlock(sc); + } + else + { + sc.MustGetToken('{'); + return true; + } + } + return false; +} +static TArray PSR_ReadBaseInstalls(FScanner &sc) +{ + TArray result; + + // Get a list of possible install directories. + while(sc.GetToken()) + { + if(sc.TokenType == '}') + break; + + sc.TokenMustBe(TK_StringConst); + FString key(sc.String); + if(key.Left(18).CompareNoCase("BaseInstallFolder_") == 0) + { + sc.MustGetToken(TK_StringConst); + result.Push(FString(sc.String) + "/steamapps/common"); + } + else + { + if(sc.CheckToken('{')) + PSR_FindEndBlock(sc); + else + sc.MustGetToken(TK_StringConst); + } + } + + return result; +} +static TArray ParseSteamRegistry(const char* path) +{ + TArray dirs; + + // Read registry data + FScanner sc; + sc.OpenFile(path); + sc.SetCMode(true); + + // Find the SteamApps listing + if(PSR_FindAndEnterBlock(sc, "InstallConfigStore")) + { + if(PSR_FindAndEnterBlock(sc, "Software")) + { + if(PSR_FindAndEnterBlock(sc, "Valve")) + { + if(PSR_FindAndEnterBlock(sc, "Steam")) + { + dirs = PSR_ReadBaseInstalls(sc); + } + PSR_FindEndBlock(sc); + } + PSR_FindEndBlock(sc); + } + PSR_FindEndBlock(sc); + } + + return dirs; +} + +static struct SteamAppInfo +{ + const char* const BasePath; + const int AppID; +} AppInfo[] = +{ + /*{"doom 2/base", 2300}, + {"final doom/base", 2290}, + {"heretic shadow of the serpent riders/base", 2390}, + {"hexen/base", 2360}, + {"hexen deathkings of the dark citadel/base", 2370}, + {"ultimate doom/base", 2280}, + {"DOOM 3 BFG Edition/base/wads", 208200},*/ + {"Strife", 317040} +}; + +TArray I_GetSteamPath() +{ + TArray result; + TArray SteamInstallFolders; + + // Linux and OS X actually allow the user to install to any location, so + // we need to figure out on an app-by-app basis where the game is installed. + // To do so, we read the virtual registry. +#ifdef __APPLE__ + FString OSX_FindApplicationSupport(); + + FString regPath = OSX_FindApplicationSupport() + "/Steam/config/config.vdf"; + try + { + SteamInstallFolders = ParseSteamRegistry(regPath); + } + catch(class CDoomError &error) + { + // If we can't parse for some reason just pretend we can't find anything. + return result; + } + + SteamInstallFolders.Push(OSX_FindApplicationSupport() + "/Steam/SteamApps/common"); +#else + char* home = getenv("HOME"); + if(home != NULL && *home != '\0') + { + FString regPath; + regPath.Format("%s/.local/share/Steam/config/config.vdf", home); + try + { + SteamInstallFolders = ParseSteamRegistry(regPath); + } + catch(class CDoomError &error) + { + // If we can't parse for some reason just pretend we can't find anything. + return result; + } + + regPath.Format("%s/.local/share/Steam/SteamApps/common", home); + SteamInstallFolders.Push(regPath); + } +#endif + + for(unsigned int i = 0;i < SteamInstallFolders.Size();++i) + { + for(unsigned int app = 0;app < countof(AppInfo);++app) + { + struct stat st; + FString candidate(SteamInstallFolders[i] + "/" + AppInfo[app].BasePath); + if(stat(candidate, &st) == 0 && S_ISDIR(st.st_mode)) + result.Push(candidate); + } + } + + return result; +} diff --git a/src/sdl/i_system.h b/src/sdl/i_system.h index a3341f4c5..fa03d3c37 100644 --- a/src/sdl/i_system.h +++ b/src/sdl/i_system.h @@ -120,6 +120,10 @@ void I_SetIWADInfo (); // Pick from multiple IWADs to use int I_PickIWad (WadStuff *wads, int numwads, bool queryiwad, int defaultiwad); +// [RH] Checks the registry for Steam's install path, so we can scan its +// directories for IWADs if the user purchased any through Steam. +TArray I_GetSteamPath(); + // The ini could not be saved at exit bool I_WriteIniFailed (); diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index a668e589b..2a2da343e 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -2354,18 +2354,33 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_SetTranslucent) // Fades the actor in // //=========================================================================== + +enum FadeFlags +{ + FTF_REMOVE = 1 << 0, + FTF_CLAMP = 1 << 1, +}; + DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeIn) { ACTION_PARAM_START(1); ACTION_PARAM_FIXED(reduce, 0); + ACTION_PARAM_INT(flags, 1); if (reduce == 0) { - reduce = FRACUNIT/10; + reduce = FRACUNIT / 10; } self->RenderStyle.Flags &= ~STYLEF_Alpha1; self->alpha += reduce; - // Should this clamp alpha to 1.0? + + if (self->alpha >= (FRACUNIT * 1)) + { + if (flags & FTF_CLAMP) + self->alpha = (FRACUNIT * 1); + if (flags & FTF_REMOVE) + self->Destroy(); + } } //=========================================================================== @@ -2379,7 +2394,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeOut) { ACTION_PARAM_START(2); ACTION_PARAM_FIXED(reduce, 0); - ACTION_PARAM_BOOL(remove, 1); + ACTION_PARAM_INT(flags, 1); if (reduce == 0) { @@ -2387,9 +2402,12 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeOut) } self->RenderStyle.Flags &= ~STYLEF_Alpha1; self->alpha -= reduce; - if (self->alpha <= 0 && remove) + if (self->alpha <= 0) { - self->Destroy(); + if (flags & FTF_CLAMP) + self->alpha = 0; + if (flags & FTF_REMOVE) + self->Destroy(); } } @@ -2406,7 +2424,7 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeTo) ACTION_PARAM_START(3); ACTION_PARAM_FIXED(target, 0); ACTION_PARAM_FIXED(amount, 1); - ACTION_PARAM_BOOL(remove, 2); + ACTION_PARAM_INT(flags, 2); self->RenderStyle.Flags &= ~STYLEF_Alpha1; @@ -2428,7 +2446,14 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_FadeTo) self->alpha = target; } } - if (self->alpha == target && remove) + if (flags & FTF_CLAMP) + { + if (self->alpha > (FRACUNIT * 1)) + self->alpha = (FRACUNIT * 1); + else if (self->alpha < 0) + self->alpha = 0; + } + if (self->alpha == target && (flags & FTF_REMOVE)) { self->Destroy(); } @@ -3046,37 +3071,41 @@ DEFINE_ACTION_FUNCTION(AActor, A_ClearTarget) enum CLOF_flags { - CLOFF_NOAIM_VERT = 0x1, - CLOFF_NOAIM_HORZ = 0x2, + CLOFF_NOAIM_VERT = 0x00000001, + CLOFF_NOAIM_HORZ = 0x00000002, - CLOFF_JUMPENEMY = 0x4, - CLOFF_JUMPFRIEND = 0x8, - CLOFF_JUMPOBJECT = 0x10, - CLOFF_JUMPNONHOSTILE = 0x20, + CLOFF_JUMPENEMY = 0x00000004, + CLOFF_JUMPFRIEND = 0x00000008, + CLOFF_JUMPOBJECT = 0x00000010, + CLOFF_JUMPNONHOSTILE = 0x00000020, - CLOFF_SKIPENEMY = 0x40, - CLOFF_SKIPFRIEND = 0x80, - CLOFF_SKIPOBJECT = 0x100, - CLOFF_SKIPNONHOSTILE = 0x200, + CLOFF_SKIPENEMY = 0x00000040, + CLOFF_SKIPFRIEND = 0x00000080, + CLOFF_SKIPOBJECT = 0x00000100, + CLOFF_SKIPNONHOSTILE = 0x00000200, - CLOFF_MUSTBESHOOTABLE = 0x400, + CLOFF_MUSTBESHOOTABLE = 0x00000400, - CLOFF_SKIPTARGET = 0x800, - CLOFF_ALLOWNULL = 0x1000, - CLOFF_CHECKPARTIAL = 0x2000, + CLOFF_SKIPTARGET = 0x00000800, + CLOFF_ALLOWNULL = 0x00001000, + CLOFF_CHECKPARTIAL = 0x00002000, - CLOFF_MUSTBEGHOST = 0x4000, - CLOFF_IGNOREGHOST = 0x8000, + CLOFF_MUSTBEGHOST = 0x00004000, + CLOFF_IGNOREGHOST = 0x00008000, - CLOFF_MUSTBESOLID = 0x10000, - CLOFF_BEYONDTARGET = 0x20000, + CLOFF_MUSTBESOLID = 0x00010000, + CLOFF_BEYONDTARGET = 0x00020000, - CLOFF_FROMBASE = 0x40000, - CLOFF_MUL_HEIGHT = 0x80000, - CLOFF_MUL_WIDTH = 0x100000, + CLOFF_FROMBASE = 0x00040000, + CLOFF_MUL_HEIGHT = 0x00080000, + CLOFF_MUL_WIDTH = 0x00100000, - CLOFF_JUMP_ON_MISS = 0x200000, - CLOFF_AIM_VERT_NOOFFSET = 0x400000, + CLOFF_JUMP_ON_MISS = 0x00200000, + CLOFF_AIM_VERT_NOOFFSET = 0x00400000, + + CLOFF_SETTARGET = 0x00800000, + CLOFF_SETMASTER = 0x01000000, + CLOFF_SETTRACER = 0x02000000, }; struct LOFData @@ -3316,6 +3345,13 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckLOF) { return; } + if ((trace.HitType == TRACE_HitActor) && (trace.Actor != NULL) && !(lof_data.BadActor)) + { + if (flags & (CLOFF_SETTARGET)) self->target = trace.Actor; + if (flags & (CLOFF_SETMASTER)) self->master = trace.Actor; + if (flags & (CLOFF_SETTRACER)) self->tracer = trace.Actor; + } + ACTION_JUMP(jump); } } @@ -3736,11 +3772,17 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_CheckFlag) // A_RaiseMaster // //=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RaiseMaster) +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RaiseMaster) { + ACTION_PARAM_START(1); + ACTION_PARAM_BOOL(copy, 0); + if (self->master != NULL) { - P_Thing_Raise(self->master); + if (copy) + P_Thing_Raise(self->master, self); + else + P_Thing_Raise(self->master, NULL); } } @@ -3749,8 +3791,10 @@ DEFINE_ACTION_FUNCTION(AActor, A_RaiseMaster) // A_RaiseChildren // //=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RaiseChildren) +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RaiseChildren) { + ACTION_PARAM_START(1); + ACTION_PARAM_BOOL(copy, 0); TThinkerIterator it; AActor *mo; @@ -3758,7 +3802,10 @@ DEFINE_ACTION_FUNCTION(AActor, A_RaiseChildren) { if (mo->master == self) { - P_Thing_Raise(mo); + if (copy) + P_Thing_Raise(mo, self); + else + P_Thing_Raise(mo, NULL); } } } @@ -3768,8 +3815,10 @@ DEFINE_ACTION_FUNCTION(AActor, A_RaiseChildren) // A_RaiseSiblings // //=========================================================================== -DEFINE_ACTION_FUNCTION(AActor, A_RaiseSiblings) +DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_RaiseSiblings) { + ACTION_PARAM_START(1); + ACTION_PARAM_BOOL(copy, 0); TThinkerIterator it; AActor *mo; @@ -3779,7 +3828,10 @@ DEFINE_ACTION_FUNCTION(AActor, A_RaiseSiblings) { if (mo->master == self->master && mo != self) { - P_Thing_Raise(mo); + if (copy) + P_Thing_Raise(mo, self); + else + P_Thing_Raise(mo, NULL); } } } diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index e8cb91239..fbe28fb5b 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -247,6 +247,12 @@ static FFlagDef ActorFlags[]= DEFINE_FLAG(MF7, DONTTHRUST, AActor, flags7), DEFINE_FLAG(MF7, ALLOWPAIN, AActor, flags7), DEFINE_FLAG(MF7, CAUSEPAIN, AActor, flags7), + DEFINE_FLAG(MF7, THRUREFLECT, AActor, flags7), + DEFINE_FLAG(MF7, MIRRORREFLECT, AActor, flags7), + DEFINE_FLAG(MF7, AIMREFLECT, AActor, flags7), + DEFINE_FLAG(MF7, HITTARGET, AActor, flags7), + DEFINE_FLAG(MF7, HITMASTER, AActor, flags7), + DEFINE_FLAG(MF7, HITTRACER, AActor, flags7), // Effect flags DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects), diff --git a/src/thingdef/thingdef_exp.cpp b/src/thingdef/thingdef_exp.cpp index 773f7ffac..daa7f1a9c 100644 --- a/src/thingdef/thingdef_exp.cpp +++ b/src/thingdef/thingdef_exp.cpp @@ -371,6 +371,37 @@ static FxExpression *ParseExpression0 (FScanner &sc, const PClass *cls) return new FxRandom(rng, min, max, sc); } + else if (sc.CheckToken(TK_Pick)) + { + FRandom *rng; + TArray list; + list.Clear(); + int index = 0; + + if (sc.CheckToken('[')) + { + sc.MustGetToken(TK_Identifier); + rng = FRandom::StaticFindRNG(sc.String); + sc.MustGetToken(']'); + } + else + { + rng = &pr_exrandom; + } + sc.MustGetToken('('); + + while (!(sc.CheckToken(')'))) + { + FxExpression *min = ParseExpressionM(sc, cls); + list.Push(min); + if (sc.CheckToken(')')) + break; + else + sc.MustGetToken(','); + } + + return new FxPick(rng, list, sc); + } else if (sc.CheckToken(TK_FRandom)) { FRandom *rng; diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index 77af37f53..0e03c661f 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -559,6 +559,27 @@ public: // //========================================================================== +class FxPick : public FxExpression +{ +protected: + FRandom * rng; + TDeletingArray min; + +public: + + FxPick(FRandom *, TArray mi, const FScriptPosition &pos); + ~FxPick(); + FxExpression *Resolve(FCompileContext&); + + ExpVal EvalExpression(AActor *self); +}; + +//========================================================================== +// +// +// +//========================================================================== + class FxFRandom : public FxRandom { public: diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 7dbf5ec36..913079bd2 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -1691,6 +1691,73 @@ ExpVal FxRandom::EvalExpression (AActor *self) return val; } +//========================================================================== +// +// +// +//========================================================================== +FxPick::FxPick(FRandom * r, TArray mi, const FScriptPosition &pos) +: FxExpression(pos) +{ + for (unsigned int index = 0; index < mi.Size(); index++) + { + min.Push(new FxIntCast(mi[index])); + } + rng = r; + ValueType = VAL_Int; +} + +//========================================================================== +// +// +// +//========================================================================== + +FxPick::~FxPick() +{ +} + +//========================================================================== +// +// +// +//========================================================================== + +FxExpression *FxPick::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + for (unsigned int index = 0; index < min.Size(); index++) + { + RESOLVE(min[index], ctx); + ABORT(min[index]); + } + return this; +}; + + +//========================================================================== +// +// +// +//========================================================================== + +ExpVal FxPick::EvalExpression(AActor *self) +{ + ExpVal val; + val.Type = VAL_Int; + int max = min.Size(); + if (max > 0) + { + int select = (*rng)(max); + val.Int = min[select]->EvalExpression(self).GetInt(); + } + else + { + val.Int = (*rng)(); + } + return val; +} + //========================================================================== // // diff --git a/src/thingdef/thingdef_properties.cpp b/src/thingdef/thingdef_properties.cpp index b5fdc8e42..b548d4ef6 100644 --- a/src/thingdef/thingdef_properties.cpp +++ b/src/thingdef/thingdef_properties.cpp @@ -2050,6 +2050,11 @@ DEFINE_CLASS_PROPERTY_PREFIX(powerup, color, C_f, Inventory) *pBlendColor = MakeSpecialColormap(v); return; } + else if (!stricmp(name, "none") && info->Class->IsDescendantOf(RUNTIME_CLASS(APowerupGiver))) + { + *pBlendColor = MakeSpecialColormap(65535); + return; + } color = V_GetColor(NULL, name); } diff --git a/src/win32/i_system.cpp b/src/win32/i_system.cpp index 4248df274..71cc42af0 100644 --- a/src/win32/i_system.cpp +++ b/src/win32/i_system.cpp @@ -1524,20 +1524,36 @@ static bool QueryPathKey(HKEY key, const char *keypath, const char *valname, FSt // //========================================================================== -FString I_GetSteamPath() +TArray I_GetSteamPath() { + TArray result; + static const char *const steam_dirs[] = + { + "doom 2/base", + "final doom/base", + "heretic shadow of the serpent riders/base", + "hexen/base", + "hexen deathkings of the dark citadel/base", + "ultimate doom/base", + "DOOM 3 BFG Edition/base/wads", + "Strife" + }; + FString path; - if (QueryPathKey(HKEY_CURRENT_USER, "Software\\Valve\\Steam", "SteamPath", path)) + if (!QueryPathKey(HKEY_CURRENT_USER, "Software\\Valve\\Steam", "SteamPath", path)) { - return path; + if (!QueryPathKey(HKEY_LOCAL_MACHINE, "Software\\Valve\\Steam", "InstallPath", path)) + return result; } - if (QueryPathKey(HKEY_LOCAL_MACHINE, "Software\\Valve\\Steam", "InstallPath", path)) + path += "/SteamApps/common/"; + + for(unsigned int i = 0; i < countof(steam_dirs); ++i) { - return path; + result.Push(path + steam_dirs[i]); } - path = ""; - return path; + + return result; } //========================================================================== diff --git a/src/win32/i_system.h b/src/win32/i_system.h index 9fbf2db5c..647a08d13 100644 --- a/src/win32/i_system.h +++ b/src/win32/i_system.h @@ -142,7 +142,7 @@ void I_SetWndProc(); // [RH] Checks the registry for Steam's install path, so we can scan its // directories for IWADs if the user purchased any through Steam. -FString I_GetSteamPath(); +TArray I_GetSteamPath(); // Damn Microsoft for doing Get/SetWindowLongPtr half-assed. Instead of // giving them proper prototypes under Win32, they are just macros for diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 468b0f7b5..2f97c2a71 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -222,9 +222,9 @@ ACTOR Actor native //: Thinker action native A_Log(string whattoprint); action native A_LogInt(int whattoprint); action native A_SetTranslucent(float alpha, int style = 0); - action native A_FadeIn(float reduce = 0.1); - action native A_FadeOut(float reduce = 0.1, bool remove = true); - action native A_FadeTo(float target, float amount = 0.1, bool remove = false); + action native A_FadeIn(float reduce = 0.1, int flags = 0); + action native A_FadeOut(float reduce = 0.1, int flags = 1); //bool remove == true + action native A_FadeTo(float target, float amount = 0.1, int flags = 0); action native A_SetScale(float scalex, float scaley = 0); action native A_SetMass(int mass); action native A_SpawnDebris(class spawntype, bool transfer_translation = false, float mult_h = 1, float mult_v = 1); @@ -241,9 +241,9 @@ ACTOR Actor native //: Thinker action native A_KillMaster(name damagetype = "none", int flags = 0); action native A_KillChildren(name damagetype = "none", int flags = 0); action native A_KillSiblings(name damagetype = "none", int flags = 0); - action native A_RaiseMaster(); - action native A_RaiseChildren(); - action native A_RaiseSiblings(); + action native A_RaiseMaster(bool copy = 0); + action native A_RaiseChildren(bool copy = 0); + action native A_RaiseSiblings(bool copy = 0); action native A_CheckFloor(state label); action native A_CheckCeiling(state label); action native A_PlayerSkinCheck(state label); diff --git a/wadsrc/static/actors/constants.txt b/wadsrc/static/actors/constants.txt index d9da11b2d..8695c9c19 100644 --- a/wadsrc/static/actors/constants.txt +++ b/wadsrc/static/actors/constants.txt @@ -372,6 +372,10 @@ enum CLOFF_JUMP_ON_MISS = 0x200000, CLOFF_AIM_VERT_NOOFFSET = 0x400000, + CLOFF_SETTARGET = 0x800000, + CLOFF_SETMASTER = 0x1000000, + CLOFF_SETTRACER = 0x2000000, + CLOFF_SKIPOBSTACLES = CLOFF_SKIPENEMY|CLOFF_SKIPFRIEND|CLOFF_SKIPOBJECT|CLOFF_SKIPNONHOSTILE, CLOFF_NOAIM = CLOFF_NOAIM_VERT|CLOFF_NOAIM_HORZ }; @@ -405,6 +409,13 @@ enum RMVF_EVERYTHING = 1 << 3, }; +// Flags for A_Fade* +enum +{ + FTF_REMOVE = 1 << 0, + FTF_CLAMP = 1 << 1, +}; + // This is only here to provide one global variable for testing. native int testglobalvar; diff --git a/wadsrc/static/actors/strife/crusader.txt b/wadsrc/static/actors/strife/crusader.txt index 7f7a0d0c5..eb1ceec6d 100644 --- a/wadsrc/static/actors/strife/crusader.txt +++ b/wadsrc/static/actors/strife/crusader.txt @@ -99,7 +99,7 @@ ACTOR CrusaderMissile Loop Death: SMIS A 0 Bright A_SetTranslucent(1,1) - SMIS A 5 Bright A_StopSoundEx("Voice") + SMIS A 5 Bright SMIS B 5 Bright SMIS C 4 Bright SMIS DEFG 2 Bright diff --git a/wadsrc/static/actors/strife/strifebishop.txt b/wadsrc/static/actors/strife/strifebishop.txt index dcedf7f58..6f38247fb 100644 --- a/wadsrc/static/actors/strife/strifebishop.txt +++ b/wadsrc/static/actors/strife/strifebishop.txt @@ -85,7 +85,7 @@ ACTOR BishopMissile Loop Death: SMIS A 0 Bright A_SetTranslucent(1,1) - SMIS A 0 Bright A_StopSoundEx("Voice") + SMIS A 0 Bright // State left for savegame compatibility SMIS A 5 Bright A_Explode(64,64,1,1) SMIS B 5 Bright SMIS C 4 Bright diff --git a/wadsrc/static/actors/strife/strifeweapons.txt b/wadsrc/static/actors/strife/strifeweapons.txt index deea01a52..3b256e226 100644 --- a/wadsrc/static/actors/strife/strifeweapons.txt +++ b/wadsrc/static/actors/strife/strifeweapons.txt @@ -390,7 +390,7 @@ ACTOR MiniMissile Loop Death: SMIS A 0 Bright A_SetTranslucent(1,1) - SMIS A 0 Bright A_StopSoundEx("Voice") + SMIS A 0 Bright // State left for savegame compatibility SMIS A 5 Bright A_Explode(64,64,1,1) SMIS B 5 Bright SMIS C 4 Bright