diff --git a/src/SB/Core/x/containers.h b/src/SB/Core/x/containers.h index 94e35f322..bfaf87e35 100644 --- a/src/SB/Core/x/containers.h +++ b/src/SB/Core/x/containers.h @@ -183,6 +183,7 @@ template struct fixed_queue void back(); void pop_back(); bool empty() const; + U32 size() const; }; #endif diff --git a/src/SB/Game/zEntCruiseBubble.cpp b/src/SB/Game/zEntCruiseBubble.cpp index 632391f7b..17c4a9a66 100644 --- a/src/SB/Game/zEntCruiseBubble.cpp +++ b/src/SB/Game/zEntCruiseBubble.cpp @@ -1,12 +1,10 @@ -#include "zNPCHazard.h" #include +#include +#include #include -#include "stdio.h" -#include "zEnt.h" -#include "zRenderState.h" - #include "zCamera.h" +#include "zEnt.h" #include "zEntButton.h" #include "zEntCruiseBubble.h" #include "zEntDestructObj.h" @@ -14,341 +12,148 @@ #include "zEntTrigger.h" #include "zGameExtras.h" #include "zGlobals.h" +#include "zNPCHazard.h" #include "zNPCTypeCommon.h" #include "zPlatform.h" +#include "zRenderState.h" #include "zTalkBox.h" #include "iMath.h" + #include "xColor.h" #include "xDecal.h" #include "xFX.h" +#include "xGrid.h" #include "xMath.h" -#include "xMathInlines.h" #include "xMath3.h" +#include "xMathInlines.h" #include "xModel.h" #include "xSnd.h" -#include "xString.h" #include "xstransvc.h" +#include "xString.h" #include "xVec3.h" -// Taken from zEntCruiseBubble.s -// Defining these here makes the stringBase0 offsets match in the later functions. -static char* str1 = "Idle01"; -static char* str2 = "Idle02"; -static char* str3 = "Idle03"; -static char* str4 = "Idle04"; -static char* str5 = "Idle05"; -static char* str6 = "Idle06"; -static char* str7 = "Idle07"; -static char* str8 = "Idle08"; -static char* str9 = "Idle09"; -static char* str10 = "Idle10"; -static char* str11 = "Idle11"; -static char* str12 = "Idle12"; -static char* str13 = "Idle13"; -static char* str14 = "SlipIdle01"; -static char* str15 = "Inactive01"; -static char* str16 = "Inactive02"; -static char* str17 = "Inactive03"; -static char* str18 = "Inactive04"; -static char* str19 = "Inactive05"; -static char* str20 = "Inactive06"; -static char* str21 = "Inactive07"; -static char* str22 = "Inactive08"; -static char* str23 = "Inactive09"; -static char* str24 = "Inactive10"; -static char* str25 = "Walk01"; -static char* str26 = "Run01"; -static char* str27 = "Run02"; -static char* str28 = "Run03"; -static char* str29 = "Land01"; -static char* str30 = "LandRun01"; -static char* str31 = "LandHigh01"; -static char* str32 = "WallLand01"; -static char* str33 = "Hit01"; -static char* str34 = "Hit02"; -static char* str35 = "Hit03"; -static char* str36 = "Hit04"; -static char* str37 = "Hit05"; -static char* str38 = "SB_cruise_start"; -static char* str39 = "SB_cruise_hit"; -static char* str40 = "SB_cruise_nav_loop"; -static char* str41 = "cruise_bubble_bind.MINF"; -static char* str42 = "lightning"; -static char* str43 = "Wake Ribbon 0"; -static char* str44 = "Player|Cruise Bubble|Wake Ribbon 0|"; -static char* str45 = "Wake Ribbon 1"; -static char* str46 = "Player|Cruise Bubble|Wake Ribbon 1|"; -static char* str47 = "par_cruise_explode"; -static char* str48 = "Cruise Bubble Explosion"; -static char* str49 = "cruise_bubble_droplet_shrapnel"; -static char* str50 = ".minf"; -static char* str51 = ".dff"; -static char* str52 = "ui_3dicon_reticle"; -static char* str53 = "ui_3dicon_target_lock"; -static char* str54 = "ui_3dicon_missile_frame02"; -static char* str55 = "%02d:%02d"; -static char* str56 = "aura2"; -static char* str57 = "aim_delay"; -static char* str58 = "player.halt_time"; -static char* str59 = "player.aim.turn_speed"; -static char* str60 = "player.aim.anim_delta"; -static char* str61 = "player.fire.delay_wand"; -static char* str62 = "missle.life"; -static char* str63 = "missle.hit_dist"; -static char* str64 = "missle.crash_angle"; -static char* str65 = "missle.collide_twist"; -static char* str66 = "missle.hit_tests"; -static char* str67 = "missle.appear.delay_show"; -static char* str68 = "missle.appear.delay_fly"; -static char* str69 = "missle.appear.offset"; -static char* str70 = "missle.fly.accel"; -static char* str71 = "missle.fly.max_vel"; -static char* str72 = "missle.fly.engine_pitch_max"; -static char* str73 = "missle.fly.engine_pitch_sensitivity"; -static char* str74 = "missle.fly.flash_interval"; -static char* str75 = "missle.fly.turn.xdelta"; -static char* str76 = "missle.fly.turn.ydelta"; -static char* str77 = "missle.fly.turn.xdecay"; -static char* str78 = "missle.fly.turn.ydecay"; -static char* str79 = "missle.fly.turn.ybound"; -static char* str80 = "missle.fly.turn.roll_frac"; -static char* str81 = "missle.explode.hit_radius"; -static char* str82 = "missle.explode.hit_duration"; -static char* str83 = "camera.aim.dist"; -static char* str84 = "camera.aim.height"; -static char* str85 = "camera.aim.pitch"; -static char* str86 = "camera.aim.accel"; -static char* str87 = "camera.aim.max_vel"; -static char* str88 = "camera.aim.stick_decel"; -static char* str89 = "camera.aim.stick_accel"; -static char* str90 = "camera.aim.stick_max_vel"; -static char* str91 = "camera.aim.turn_speed"; -static char* str92 = "camera.seize.delay"; -static char* str93 = "camera.seize.blend_time"; -static char* str94 = "camera.seize.fade_dist"; -static char* str95 = "camera.seize.hide_dist"; -static char* str96 = "camera.seize.fov"; -static char* str97 = "camera.survey.duration"; -static char* str98 = "camera.survey.min_duration"; -static char* str99 = "camera.survey.min_dist"; -static char* str100 = "camera.survey.cut_dist"; -static char* str101 = "camera.survey.drift_dist"; -static char* str102 = "camera.survey.drift_softness"; -static char* str103 = "camera.survey.jerk_offset"; -static char* str104 = "camera.survey.jerk_deflect"; -static char* str105 = "camera.restore.control_delay"; -static char* str106 = "material.env_alpha"; -static char* str107 = "material.env_coeff"; -static char* str108 = "material.fresnel_alpha"; -static char* str109 = "material.fresnel_coeff"; -static char* str110 = "reticle.dist_min"; -static char* str111 = "reticle.dist_max"; -static char* str112 = "reticle.ang_show"; -static char* str113 = "reticle.ang_hide"; -static char* str114 = "reticle.delay_retarget"; -static char* str115 = "trail.sample_rate"; -static char* str116 = "trail.bubble_rate"; -static char* str117 = "trail.bubble_emit_radius"; -static char* str118 = "trail.wake_emit_radius"; -static char* str119 = "blast.emit"; -static char* str120 = "blast.radius"; -static char* str121 = "blast.vel"; -static char* str122 = "blast.rand_vel"; -static char* str123 = "droplet.dist_min"; -static char* str124 = "droplet.dist_max"; -static char* str125 = "droplet.emit_min"; -static char* str126 = "droplet.emit_max"; -static char* str127 = "droplet.vel_min"; -static char* str128 = "droplet.vel_max"; -static char* str129 = "droplet.vel_perturb"; -static char* str130 = "droplet.vel_angle"; -static char* str131 = "droplet.rot_vel_max"; -static char* str132 = "hud.glow_size"; -static char* str133 = "hud.time_fade"; -static char* str134 = "hud.time_glow"; -static char* str135 = "hud.wind.size"; -static char* str136 = "hud.wind.du"; -static char* str137 = "hud.wind.dv"; -static char* str138 = "hud.reticle.size"; -static char* str139 = "hud.target.size"; -static char* str140 = "hud.timer.font"; -static char* str141 = "hud.timer.font_width"; -static char* str142 = "hud.timer.font_height"; -static char* str143 = "hud.timer.x"; -static char* str144 = "hud.timer.y"; -static char* str145 = "hud.timer.glow_size"; -static char* str146 = "dialog.freq"; -static char* str147 = "dialog.decay"; -static char* str148 = "dialog.min_freq"; -static char* str149 = "gloss_edge"; -static char* str150 = "rainbowfilm_smooth32"; -static char* str151 = "cruise_bubble_aim"; -static char* str152 = "cruise_bubble_fire"; -static char* str153 = "cruise_bubble_idle"; -static char* str154 = "cruise_bubble_aim cruise_bubble_fire cruise_bubble_idle"; -static char* str155 = "Cruise Bubble"; -static char* str156 = "fire"; -static char* str157 = "fly"; - -namespace cruise_bubble -{ - extern basic_rect screen_bounds; - // basic_rect default_adjust; - // char buffer[16]; - // char buffer[16]; - extern const char* start_anim_states[37]; // string array of names - - extern fixed_queue missle_record; - extern xFXRibbon wake_ribbon[2]; - extern xDecalEmitter explode_decal; - extern const xFXRibbon::curve_node wake_ribbon_curve[2]; - extern const xFXRibbon::curve_node cheat_wake_ribbon_curve[2]; - extern const xDecalEmitter::curve_node explode_curve[3]; - extern const xDecalEmitter::curve_node cheat_explode_curve[3]; - extern sound_config sounds[4]; - // quadrant_set qzone; - - extern struct _class_17 - { - bool hiding; - F32 alpha; - F32 alpha_vel; - F32 glow; - F32 glow_vel; - - // Offset 0x14 - struct _class_21 - { - xModelInstance* reticle; - xModelInstance* target; - xModelInstance* swirl; - xModelInstance* wind; - } model; - // Offset 0x24 - hud_gizmo gizmo[33]; - // Offset 0x654 - U32 gizmos_used; - // Offset 0x658 - uv_animated_model uv_swirl; - // Offset 0x674 - uv_animated_model uv_wind; - } hud; - // void (*xAnimDefaultBeforeEnter)(xAnimPlay*, xAnimState*); - // U32 (*check_anim_aim)(xAnimTransition*, xAnimSingle*, void*); - // zGlobals globals; - // RpAtomic* (*AtomicDefaultRenderCallBack)(RpAtomic*); - // RpAtomic* (*custom_bubble_render)(RpAtomic*); - // iColor_tag g_WHITE; - // U32 gActiveHeap; - // _anon0 __vt__Q313cruise_bubble30 @unnamed @zEntCruiseBubble_cpp @20state_camera_restore; - // _anon5 __vt__Q313cruise_bubble30 @unnamed @zEntCruiseBubble_cpp @10state_type; - // _anon6 __vt__Q313cruise_bubble30 @unnamed @zEntCruiseBubble_cpp @19state_camera_survey; - // _anon1 __vt__Q313cruise_bubble30 @unnamed @zEntCruiseBubble_cpp @19state_camera_attach; - // _anon9 __vt__Q313cruise_bubble30 @unnamed @zEntCruiseBubble_cpp @18state_camera_seize; - // _anon10 __vt__Q313cruise_bubble30 @unnamed @zEntCruiseBubble_cpp @16state_camera_aim; - // _anon3 __vt__Q313cruise_bubble30 @unnamed @zEntCruiseBubble_cpp @20state_missle_explode; - // _anon7 __vt__Q313cruise_bubble30 @unnamed @zEntCruiseBubble_cpp @16state_missle_fly; - // _anon2 __vt__Q313cruise_bubble30 @unnamed @zEntCruiseBubble_cpp @19state_missle_appear; - // _anon4 __vt__Q313cruise_bubble30 @unnamed @zEntCruiseBubble_cpp @17state_player_wait; - // _anon8 __vt__Q313cruise_bubble30 @unnamed @zEntCruiseBubble_cpp @17state_player_fire; - // _anon11 __vt__Q313cruise_bubble30 @unnamed @zEntCruiseBubble_cpp @16state_player_aim; - // _anon12 __vt__Q313cruise_bubble30 @unnamed @zEntCruiseBubble_cpp @17state_player_halt; - // RpAtomic* (*gAtomicRenderCallBack)(RpAtomic*); - // U32 gFXSurfaceFlags; - // xVec3 m_UnitAxisY; - // xGrid npcs_grid; - // xGrid colls_oso_grid; - // xGrid colls_grid; - // U8 (*hazard_check)(NPCHazard&, void*); - // xQCControl xqc_def_ctrl; - // void (*SweptSphereHitsCameraEnt)(xScene*, xRay3*, xQCData*, xEnt*, void*); - // void (*cb_droplet)(zFrag*, zFragAsset*); - // U8 (*hazard_check)(NPCHazard&, void*); - // U8 (*hazard_check)(NPCHazard&, void*); - // S32 gGridIterActive; -} // namespace cruise_bubble - -extern F32 zEntCruiseBubble_f_0_0; // 0.0 -extern F32 zEntCruiseBubble_f_1_0; // 1.0 -extern F32 zEntCruiseBubble_f_0_5; // 0.5 -extern F32 zEntCruiseBubble_f_3_0; // 3.0 -extern F32 zEntCruiseBubble_f_0_25; // 0.25 -extern F32 zEntCruiseBubble_f_255_0; // 255.0 -extern F32 zEntCruiseBubble_f_n1_0; // -1.0 -extern F32 zEntCruiseBubble_f_100_0; // 100.0 -extern F32 zEntCruiseBubble_f_n0_00001; // -0.00001 -extern F32 zEntCruiseBubble_f_0_00001; // 0.00001 -extern F32 zEntCruiseBubble_f_0_2; // 0.2 -extern F32 zEntCruiseBubble_f_0_05; // 0.05 -extern F32 zEntCruiseBubble_f_0_01; // 0.01 -extern F32 zEntCruiseBubble_f_1000000000_0; // 1000000000.0 -extern F32 zEntCruiseBubble_f_0_0667; // 0.0667 -extern F32 zEntCruiseBubble_f_6_0; // 6.0 -extern F32 zEntCruiseBubble_f_0_3; // 0.3 -extern F32 zEntCruiseBubble_f_30_0; // 30.0 -extern F32 zEntCruiseBubble_f_0_017; // 0.017 deg2rad -extern F32 zEntCruiseBubble_f_60_0; // 60.0 -extern F32 zEntCruiseBubble_f_0_025; // 0.025 -extern F32 zEntCruiseBubble_f_0_1333; // 0.1333 -extern F32 zEntCruiseBubble_f_0_667; // 0.667 -extern F32 zEntCruiseBubble_f_n0_049; // -0.049 -extern F32 zEntCruiseBubble_f_1_728; // 1.728 -extern F32 zEntCruiseBubble_f_0_922; // 0.922 -extern F32 zEntCruiseBubble_f_12_0; // 12.0 -extern F32 zEntCruiseBubble_f_10_0; // 10.0 -extern F32 zEntCruiseBubble_f_0_005; // 0.005 -extern F32 zEntCruiseBubble_f_2_0; // 2.0 -extern F32 zEntCruiseBubble_f_5_0; // 5.0 -extern F32 zEntCruiseBubble_f_4_0; // 4.0 -extern F32 zEntCruiseBubble_f_0_1; // 0.1 -extern F32 zEntCruiseBubble_f_0_6; // 0.6 -extern F32 zEntCruiseBubble_f_1_5708; // 1.5708 90deg -extern F32 zEntCruiseBubble_f_1_5; // 1.5 -extern F32 zEntCruiseBubble_f_n10_0; // -10.0 -extern F32 zEntCruiseBubble_f_n90_0; // -90.0 -extern F32 zEntCruiseBubble_f_90_0; // 90.0 -extern F32 zEntCruiseBubble_f_720_0; // 720.0 -extern F32 zEntCruiseBubble_f_360_0; // 360.0 -extern F32 zEntCruiseBubble_f_135_0; // 135.0 -extern F32 zEntCruiseBubble_f_0_001; // 0.001 -extern F32 zEntCruiseBubble_f_95_0; // 95.0 -extern F32 zEntCruiseBubble_f_180_0; // 180.0 -extern F32 zEntCruiseBubble_f_8_0; // 8.0 -extern F32 zEntCruiseBubble_f_0_99; // 0.1 -extern F32 zEntCruiseBubble_f_0_8; // 0.8 -extern F32 zEntCruiseBubble_f_0_75; // 0.75 -extern F32 zEntCruiseBubble_f_10000_0; // 10000.0 -extern F32 zEntCruiseBubble_f_22_5; // 22.5 -extern F32 zEntCruiseBubble_f_n100000_0; // -100000.0 -extern F32 zEntCruiseBubble_f_100000_0; // 100000.0 -extern F32 zEntCruiseBubble_f_n1000000000_0; // -1000000000.0 -extern F32 zEntCruiseBubble_f_0_0275; // 0.0275 -extern F32 zEntCruiseBubble_f_0_047; // 0.047 -extern F32 zEntCruiseBubble_f_0_78; // 0.78 -extern F32 zEntCruiseBubble_f_0_86; // 0.86 -extern F32 zEntCruiseBubble_f_0_15; // 0.15 -extern F32 zEntCruiseBubble_f_0_0001; // 0.15 -extern F32 zEntCruiseBubble_f_1_0e38; // 1.0 * 10^38 -extern F32 zEntCruiseBubble_f_3_1415; // 3.1415 ~ PI -extern F32 zEntCruiseBubble_f_6_2832; // 6.2832 ~ 2PI -extern F32 zEntCruiseBubble_f_n3_1415; // -3.1415 ~ -PI -extern F32 zEntCruiseBubble_f_0_000001; // 0.000001 +basic_rect screen_bounds = { 0.0f, 0.0f, 1.0f, 1.0f }; +basic_rect default_adjust = { 0.0f, 0.0f, 1.0f, 1.0f }; extern iColor_tag zEntCruiseBubble_color_80_00_00_FF; // 128, 0, 0, 255 extern iColor_tag zEntCruiseBubble_color_FF_14_14_FF; // 255, 20, 20, 255 -extern xVec3 zEntCruiseBubble_xVec3_0_0_0; // {0.0, 0.0, 0.0} +extern RpAtomic *(*gAtomicRenderCallBack)(RpAtomic*); -extern xVec2 lbl_803D0830; +template +void xGridCheckBound(xGrid& grid, const xBound& bound, const xQCData& qcd, T param); namespace cruise_bubble { - extern tweak_group* current_tweak; - namespace { - extern struct _class_36 + tweak_group normal_tweak; + tweak_group cheat_tweak; + xMat4x3 start_cam_mat; + + const xDecalEmitter::curve_node explode_curve[3] = + { + { 0.0f, { 0xFF, 0xFF, 0xFF, 0xFF }, 0.1f }, + { 0.5f, { 0xFF, 0xFF, 0xFF, 0xFF }, 2.55f }, + { 1.0f, { 0xFF, 0xFF, 0xFF, 0x00 }, 5.0f }, + }; + + const xDecalEmitter::curve_node cheat_explode_curve[3] = + { + { 0.0f, { 0xFF, 0xFF, 0xFF, 0xFF }, 0.1f }, + { 0.5f, { 0xFF, 0xFF, 0xFF, 0xFF }, 3.55f }, + { 1.0f, { 0xFF, 0xFF, 0xFF, 0x00 }, 7.0f }, + }; + + fixed_queue missle_record; + tweak_group* current_tweak = &normal_tweak; + + xBase base = { 0, eBaseTypeCruiseBubble }; + + const char* start_anim_states[37] = + { + "Idle01", + "Idle02", + "Idle03", + "Idle04", + "Idle05", + "Idle06", + "Idle07", + "Idle08", + "Idle09", + "Idle10", + "Idle11", + "Idle12", + "Idle13", + "SlipIdle01", + "Inactive01", + "Inactive02", + "Inactive03", + "Inactive04", + "Inactive05", + "Inactive06", + "Inactive07", + "Inactive08", + "Inactive09", + "Inactive10", + "Walk01", + "Run01", + "Run02", + "Run03", + "Land01", + "LandRun01", + "LandHigh01", + "WallLand01", + "Hit01", + "Hit02", + "Hit03", + "Hit04", + "Hit05" + }; + + xFXRibbon wake_ribbon[2]; + xDecalEmitter explode_decal; + + const xFXRibbon::curve_node wake_ribbon_curve[2] = + { + { 0.0f, { 0xFF, 0xFF, 0xFF, 0x64 }, 0.3f }, + { 1.0f, { 0x00, 0x00, 0x00, 0x00 }, 1.0f }, + }; + + const xFXRibbon::curve_node cheat_wake_ribbon_curve[2] = + { + { 0.0f, { 0xFF, 0x9B, 0x9B, 0x64 }, 0.5f }, + { 1.0f, { 0x00, 0x00, 0x00, 0x00 }, 2.0f }, + }; + + struct + { + bool hiding; + F32 alpha; + F32 alpha_vel; + F32 glow; + F32 glow_vel; + + // Offset 0x14 + struct + { + xModelInstance* reticle; + xModelInstance* target; + xModelInstance* swirl; + xModelInstance* wind; + } model; + // Offset 0x24 + hud_gizmo gizmo[33]; + // Offset 0x654 + U32 gizmos_used; + // Offset 0x658 + uv_animated_model uv_swirl; + // Offset 0x674 + uv_animated_model uv_wind; + } hud; + + struct { S32 flags; state_type* state[MAX_THREAD]; @@ -374,48 +179,54 @@ namespace cruise_bubble F32 fov_default; zShrapnelAsset* droplet_shrapnel; F32 dialog_freq; - struct _class_45 + struct { F32 samples; F32 bubbles; xMat4x3 mat; xQuat dir; } trail; - struct _class_6 + struct { - struct _class_8 + struct { xAnimState* aim; xAnimState* fire; xAnimState* idle; } player; - struct _class_16 + struct { // Offset: 0x170 xAnimState* fire; xAnimState* fly; } missle; } astate; - struct _class_25 + struct { - struct _class_28 + struct { xAnimTransition* aim; xAnimTransition* fire; xAnimTransition* idle; xAnimTransition* end; } player; - struct _class_33 + struct { xAnimTransition* fly; } missle; } atran; - } shared; + } shared = + { + 1 // flags + }; - xMat4x3 start_cam_mat; - tweak_group normal_tweak; - tweak_group cheat_tweak; - extern xBase base; + sound_config sounds[4] = + { + { "SB_cruise_start", 1.0f, 5.0f, 20.0f, 0, 0, SDR_CruiseBubbleLaunch, 0, 0, 0, 0 }, + { "SB_cruise_hit", 1.0f, 15.0f, 60.0f, 0, 0, SDR_CruiseBubbleExplode, 0, 0, 0, 0 }, + { "SB_cruise_nav_loop", 1.0f, 0.0f, 10.0f, 0, 1, SDR_None, 0, 0, 0, 0 }, + { NULL, 1.0f, 0.0f, 0.0f, 1, 0, SDR_None, 23, 25, 0, 0 }, + }; void init_sound() { @@ -466,7 +277,7 @@ namespace cruise_bubble S32 n = s->last - s->first + 1; S32 i = n <= 1 ? s->first : s->first + (xrand() >> 13) % n; - if (s->streamed != 0) + if (s->streamed) { zEntPlayer_SNDPlayStream((_tagePlayerStreamSnd)i); } @@ -509,6 +320,30 @@ namespace cruise_bubble return s->handle; } + void set_pitch(S32 which, F32 pitch, U32 handle) + { + sound_config* s = &sounds[which]; + + if (s->id == 0) + { + for (S32 i = s->first; i <= s->last; ++i) + { + zEntPlayer_SNDSetPitch((_tagePlayerSnd)i, pitch); + } + return; + } + + if (handle == 0) + { + handle = s->handle; + } + + if (handle != 0) + { + xSndSetPitch(handle, pitch); + } + } + void show_wand() { globals.player.sb_models[5]->Flags |= 0x0001; @@ -545,7 +380,7 @@ namespace cruise_bubble bool camera_taken() { - return zCameraGetConvers() != 0 || (zCameraIsTrackingDisabled() & 0xfffffffd) != 0; + return zCameraGetConvers() != 0 || (zCameraIsTrackingDisabled() & 0xfffffffd); } bool camera_leave() @@ -558,18 +393,106 @@ namespace cruise_bubble shared.hits_size = 0; } + + static void damage_entity(xEnt& ent, const xVec3& loc, const xVec3& dir, const xVec3& hit_norm, + F32 radius, bool explosive) + { + if (shared.hits_size >= 32) + { + return; + } + shared.hits[shared.hits_size] = &ent; + shared.hits_size++; + + switch (ent.baseType) + { + case eBaseTypeButton: + zEntButton_Press((_zEntButton*)&ent, 0x10); + return; + + case eBaseTypeDestructObj: + zEntDestructObj_Hit((zEntDestructObj*)&ent, 0x10000); + return; + + case eBaseTypePlatform: + switch (ent.subType) + { + case ZPLATFORM_SUBTYPE_PADDLE: + if (!(((zPlatform*)&ent)->passet->paddle.paddleFlags & 0x20)) + { + return; + } + + xCollis coll; + coll.optr = &ent; + coll.mptr = ent.collModel != NULL ? ent.collModel : ent.model; + + if (explosive) + { + coll.flags = 0x600; + + xSphere o; + o.center = loc; + o.r = radius; + xSphereHitsBound(&o, &ent.bound, &coll); + + if (!(coll.flags & 0x1)) + { + return; + } + + if (ent.collLev == 0x5) + { + xSphereHitsModel(&o, coll.mptr, &coll); + + if (!(coll.flags & 0x1)) + { + return; + } + } + + xVec3 hit_dir = coll.tohit.up_normal(); + zPlatform_PaddleCollide(&coll, &loc, &hit_dir, 0x1); + return; + } + + coll.flags = 0x201; + coll.norm = hit_norm; + zPlatform_PaddleCollide(&coll, &loc, &dir, 1); + return; + } + break; + + case eBaseTypeNPC: + if (explosive) + { + // fuck this... weird scheduling + xVec3 edir = (*xEntGetCenter(&ent) - loc).up_normal(); + ((zNPCCommon*)&ent)->Damage(DMGTYP_CRUISEBUBBLE, &base, &edir); + } + else + { + // while this matches + ((zNPCCommon*)&ent)->Damage(DMGTYP_CRUISEBUBBLE, &base, &dir); + } + return; + } + + zEntEvent(&ent, eEventHit_Cruise); + } + U8 can_damage(xEnt* ent) { if (ent == NULL) { return false; } - if ((ent->moreFlags & 0x10) == 0) + if (!(ent->moreFlags & 0x10)) { return false; } if (ent->baseType == eBaseTypeDestructObj && - zEntDestructObj_isDestroyed((zEntDestructObj*)ent) != 0) + zEntDestructObj_isDestroyed((zEntDestructObj*)ent) != NULL) { return false; } @@ -621,17 +544,17 @@ namespace cruise_bubble xLinkAsset* end_link = link + trig.linkCount; for (; link != end_link; ++link) { - if (link->srcEvent == 0x201) + if (link->srcEvent == eEventEnterCruise) { want_enter = true; } - else if (link->srcEvent == 0x202) + else if (link->srcEvent == eEventExitCruise) { want_exit = true; } } - want_enter = want_enter && (trig.entered & 0x2) == 0; - want_exit = want_exit && (trig.entered & 0x2) != 0; + want_enter = want_enter && !(trig.entered & 0x2); + want_exit = want_exit && (trig.entered & 0x2); if (want_enter || want_exit) { @@ -647,11 +570,11 @@ namespace cruise_bubble if (want_enter && inside) { - zEntEvent(&trig, 0x201); + zEntEvent(&trig, eEventEnterCruise); } else if (want_exit && !inside) { - zEntEvent(&trig, 0x202); + zEntEvent(&trig, eEventExitCruise); } } } @@ -676,7 +599,7 @@ namespace cruise_bubble { zEntTriggerAsset(*trig); - if ((trig->entered & 0x2) != 0) + if (trig->entered & 0x2) { trig->entered &= 0xfffffffd; @@ -684,9 +607,9 @@ namespace cruise_bubble end_link = link + trig->linkCount; for (; link != end_link; ++link) { - if (link->srcEvent == 0x202) + if (link->srcEvent == eEventExitCruise) { - zEntEvent(trig, 0x202); + zEntEvent(trig, eEventExitCruise); break; } } @@ -741,83 +664,98 @@ namespace cruise_bubble } } - U32 check_anim_aim(xAnimTransition*, xAnimSingle*, void*) + void cruise_bubble::state_type::start() { - return false; + // empty } - void check_lock_target(const xVec3* target) + void cruise_bubble::state_type::stop() { - xMat4x3* mat = &globals.camera.mat; - xVec3 offset = *target - mat->pos; - F32 ang = offset.dot(mat->at); + // empty + } - if (ang < current_tweak->reticle.dist_min || ang > current_tweak->reticle.dist_max) - { - return; - } + bool check_launch() + { + bool can_cruise_bubble = + (!(globals.player.ControlOff || globals.player.cheat_mode) && + globals.player.g.PowerUp[1] && (globals.player.s->pcType == ePlayer_SB) && + (globals.pad0->pressed & 0x100)); - ang = offset.length(); - if ((ang >= zEntCruiseBubble_f_n0_00001) && (ang <= zEntCruiseBubble_f_0_00001)) - { - ang = zEntCruiseBubble_f_0_0; - } - else + if (!can_cruise_bubble) { - ang = xacos(offset.dot(mat->at) / ang); + return false; } - F32 max_ang = current_tweak->reticle.ang_show; - F32 min_ang = current_tweak->reticle.ang_hide; - - if (ang >= min_ang) + xAnimState* state = globals.player.ent.model->Anim->Single->State; + for (U32 i = 0; i < 37; ++i) { - return; + if (stricmp(start_anim_states[i], state->Name) == 0) + { + return true; + } } - if (ang >= max_ang) + return false; + } + + void kill(bool reset_camera, bool abortive) + { + if (abortive) { - S32 t = find_locked_target(target); - if (t < 0) + for (S32 i = THREAD_PLAYER; i < MAX_THREAD; ++i) { - return; + if (shared.state[i] != NULL) + { + (shared.state[i])->abort(); + shared.state[i] = NULL; + } } - - lock_target(t, target, (min_ang - ang) / (min_ang - max_ang)); } - else { - lock_target(find_locked_target(target), target, zEntCruiseBubble_f_1_0); + for (S32 i = THREAD_PLAYER; i < MAX_THREAD; ++i) + { + // either STATE_INVALID or BACKUP_STATE_PLAYER, both == 0x11111111 + set_state((thread_enum)i, STATE_INVALID); + } } - } - S32 find_locked_target(const xVec3* target) - { - for (S32 i = 1; i < hud.gizmos_used; ++i) + shared.flags = 0x3; + zCameraEnableTracking(CO_CRUISE_BUBBLE); + ztalkbox::permit(0xffffffff, 0); + if (reset_camera) { - if (hud.gizmo[i].target == target) - { - return i; - } + zCameraEnableInput(); + xCameraSetFOV(&globals.camera, shared.fov_default); } - return -1; - } + hide_wand(); + hide_missle(); + hide_hud(); + stop_trail(); + distort_screen(0.0f); + xSndSelectListenerMode(SND_LISTENER_MODE_PLAYER); - xMat4x3* get_missle_mat() - { - return (xMat4x3*)shared.missle_model->Mat; + xAnimState* state = globals.player.ent.model->Anim->Single->State; + if (state == shared.astate.player.aim || state == shared.astate.player.fire || + state == shared.astate.player.idle) + { + xAnimPlayStartTransition( + globals.player.ent.model->Anim, + shared.atran.player + .end); // [xAnimPlayStartTransition__FP9xAnimPlayP15xAnimTransition] + } + exit_triggers(*globals.sceneCur); } - xMat4x3* get_player_mat() + void distort_screen(F32) { - return (xMat4x3*)globals.player.ent.model->Mat; + // empty } - xVec3& get_player_loc() + void state_type::abort() { - return *(xVec3*)&globals.player.ent.model->Mat->pos; + // empty } void update_player(xScene& s, F32 dt) @@ -833,7 +771,7 @@ namespace cruise_bubble { shared.player_motion += cruise_bubble::get_player_loc() - pre_update_loc - drive_motion; - if (shared.player_motion.length2() > zEntCruiseBubble_f_0_25) + if (shared.player_motion.length2() > 0.25f) { stop = true; } @@ -845,6 +783,11 @@ namespace cruise_bubble } } + xVec3& get_player_loc() + { + return *(xVec3*)&globals.player.ent.model->Mat->pos; + } + void render_player() { zEntPlayer_MinimalRender(&globals.player.ent); @@ -898,316 +841,244 @@ namespace cruise_bubble } } - void init_states() - { - static state_player_halt player_halt; - shared.states[STATE_PLAYER_HALT] = &player_halt; + void state_type::render() + { + // empty + } + + RpAtomic* custom_bubble_render(RpAtomic* atomic) + { + RwCullMode old_cull_mode; + F32 fade = shared.missle_model->Alpha; + F32 fresnel_coeff; + F32 env_coeff; + + RwRenderStateGet(rwRENDERSTATECULLMODE, (void*)&old_cull_mode); + RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)1); + iDrawSetFBMSK(0xFFFFFFFF); + AtomicDisableMatFX(atomic); + RpSkinAtomicSetType(atomic, rpSKINTYPEMATFX); + (*gAtomicRenderCallBack)(atomic); + iDrawSetFBMSK(0); + if (current_tweak->material.fresnel_texture != 0) + { + fresnel_coeff = current_tweak->material.fresnel_coeff * fade; + iModelSetMaterialAlpha(atomic, (S32)(current_tweak->material.fresnel_alpha * 255.0f * fade + 0.5f) & 0xFF); + gFXSurfaceFlags = 0x10; + xFXAtomicEnvMapSetup(atomic, current_tweak->material.fresnel_texture, fresnel_coeff); + gFXSurfaceFlags = 0; + (*gAtomicRenderCallBack)(atomic); + } + if (current_tweak->material.env_texture != 0) + { + env_coeff = current_tweak->material.env_coeff * fade; + iModelSetMaterialAlpha(atomic, (S32)(current_tweak->material.env_alpha * 255.0f * fade + 0.5f) & 0xFF); + AtomicDisableMatFX(atomic); + gFXSurfaceFlags = 0x10; + xFXAtomicEnvMapSetup(atomic, current_tweak->material.env_texture, env_coeff); + gFXSurfaceFlags = 0; + (*gAtomicRenderCallBack)(atomic); + } + RwRenderStateSet(rwRENDERSTATECULLMODE, (void*)old_cull_mode); + + return atomic; + } - static state_player_aim player_aim; - shared.states[STATE_PLAYER_AIM] = &player_aim; + void init_states() + { + static state_player_halt player_halt; + shared.states[STATE_PLAYER_HALT] = &player_halt; - static state_player_fire player_fire; - shared.states[STATE_PLAYER_FIRE] = &player_fire; + static state_player_aim player_aim; + shared.states[STATE_PLAYER_AIM] = &player_aim; - static state_player_wait player_wait; - shared.states[STATE_PLAYER_WAIT] = &player_wait; + static state_player_fire player_fire; + shared.states[STATE_PLAYER_FIRE] = &player_fire; - static state_missle_appear missle_appear; - shared.states[STATE_MISSLE_APPEAR] = &missle_appear; + static state_player_wait player_wait; + shared.states[STATE_PLAYER_WAIT] = &player_wait; - static state_missle_fly missle_fly; - shared.states[STATE_MISSLE_FLY] = &missle_fly; + static state_missle_appear missle_appear; + shared.states[STATE_MISSLE_APPEAR] = &missle_appear; - static state_missle_explode missle_explode; - shared.states[STATE_MISSLE_EXPLODE] = &missle_explode; + static state_missle_fly missle_fly; + shared.states[STATE_MISSLE_FLY] = &missle_fly; - static state_camera_aim camera_aim; - shared.states[STATE_CAMERA_AIM] = &camera_aim; + static state_missle_explode missle_explode; + shared.states[STATE_MISSLE_EXPLODE] = &missle_explode; - static state_camera_seize camera_seize; - shared.states[STATE_CAMERA_SEIZE] = &camera_seize; + static state_camera_aim camera_aim; + shared.states[STATE_CAMERA_AIM] = &camera_aim; - static state_camera_attach camera_attach; - shared.states[STATE_CAMERA_ATTACH] = &camera_attach; + static state_camera_seize camera_seize; + shared.states[STATE_CAMERA_SEIZE] = &camera_seize; - static state_camera_survey camera_survey; - shared.states[STATE_CAMERA_SURVEY] = &camera_survey; + static state_camera_attach camera_attach; + shared.states[STATE_CAMERA_ATTACH] = &camera_attach; - static state_camera_restore camera_return; - shared.states[STATE_CAMERA_RESTORE] = &camera_return; - } + static state_camera_survey camera_survey; + shared.states[STATE_CAMERA_SURVEY] = &camera_survey; - void init_debug() - { - // empty + static state_camera_restore camera_return; + shared.states[STATE_CAMERA_RESTORE] = &camera_return; } - void init_shrapnel() + cruise_bubble::state_camera_restore::state_camera_restore() : state_type(STATE_CAMERA_RESTORE) { - shared.droplet_shrapnel = - (zShrapnelAsset*)xSTFindAsset(xStrHash("cruise_bubble_droplet_shrapnel"), NULL); } - void init_wake_ribbons() + cruise_bubble::state_type::state_type(cruise_bubble::state_enum type) { - wake_ribbon[0].init("Wake Ribbon 0", "Player|Cruise Bubble|Wake Ribbon 0|"); - wake_ribbon[1].init("Wake Ribbon 1", "Player|Cruise Bubble|Wake Ribbon 1|"); - cruise_bubble::reset_wake_ribbons(); + this->type = type; } - bool check_launch() + cruise_bubble::state_camera_survey::state_camera_survey() : state_type(STATE_CAMERA_SURVEY) { - bool can_cruise_bubble = - (!(globals.player.ControlOff || globals.player.cheat_mode) && - globals.player.g.PowerUp[1] && (globals.player.s->pcType == ePlayer_SB) && - (globals.pad0->pressed & 0x100)); - - if (!can_cruise_bubble) - { - return false; - } - - xAnimState* state = globals.player.ent.model->Anim->Single->State; - for (U32 i = 0; i < 37; ++i) - { - if (stricmp(start_anim_states[i], state->Name) == 0) - { - return true; - } - } - - return false; } - void kill(bool reset_camera, bool abortive) + cruise_bubble::state_camera_attach::state_camera_attach() : state_type(STATE_CAMERA_ATTACH) { - if (abortive) - { - for (S32 i = THREAD_PLAYER; i < MAX_THREAD; ++i) - { - if (shared.state[i] != NULL) - { - (shared.state[i])->abort(); - shared.state[i] = NULL; - } - } - } - else - { - for (S32 i = THREAD_PLAYER; i < MAX_THREAD; ++i) - { - // either STATE_INVALID or BACKUP_STATE_PLAYER, both == 0x11111111 - set_state((thread_enum)i, STATE_INVALID); - } - } - - shared.flags = 0x3; - zCameraEnableTracking(CO_CRUISE_BUBBLE); - ztalkbox::permit(0xffffffff, 0); - if (reset_camera) - { - zCameraEnableInput(); - xCameraSetFOV(&globals.camera, shared.fov_default); - } - - hide_wand(); - hide_missle(); - hide_hud(); - stop_trail(); - distort_screen(zEntCruiseBubble_f_0_0); - xSndSelectListenerMode(SND_LISTENER_MODE_PLAYER); - - xAnimState* state = globals.player.ent.model->Anim->Single->State; - if (state == shared.astate.player.aim || state == shared.astate.player.fire || - state == shared.astate.player.idle) - { - xAnimPlayStartTransition( - globals.player.ent.model->Anim, - shared.atran.player - .end); // [xAnimPlayStartTransition__FP9xAnimPlayP15xAnimTransition] - } - exit_triggers(*globals.sceneCur); } - void distort_screen(F32) + cruise_bubble::state_camera_seize::state_camera_seize() : state_type(STATE_CAMERA_SEIZE) { - // empty } - void lerp(U8& x, F32 t, U8 a, U8 b) + cruise_bubble::state_camera_aim::state_camera_aim() : state_type(STATE_CAMERA_AIM) { - x = zEntCruiseBubble_f_0_5 + ((F32)a + t * ((F32)b - (F32)a)); } - void update_hud(F32 dt) + cruise_bubble::state_missle_explode::state_missle_explode() : state_type(STATE_MISSLE_EXPLODE) { - if (hud.gizmos_used == 0) - { - return; - } - - hud.alpha = range_limit(hud.alpha_vel * dt + hud.alpha, zEntCruiseBubble_f_0_0, - zEntCruiseBubble_f_1_0); - hud.glow = range_limit(hud.glow_vel * dt + hud.glow, zEntCruiseBubble_f_0_0, - zEntCruiseBubble_f_1_0); - - // scheduling off - F32 vel_frac = ((state_missle_fly*)shared.states[STATE_MISSLE_FLY])->vel / - current_tweak->missle.fly.max_vel; - - hud.uv_wind.offset_vel.assign(current_tweak->hud.wind.du, current_tweak->hud.wind.dv); - hud.uv_wind.offset_vel *= vel_frac; - hud.model.wind->Alpha = vel_frac; - hud.uv_wind.update(dt); + } - // sheduling off for i and zEntCruiseBubble_f_n1_0 - for (S32 i = 1; i < hud.gizmos_used; ++i) - { - if ((hud.gizmo[i].flags & 0x1) == 0) - { - hud.gizmo[i].alpha_vel = zEntCruiseBubble_f_n1_0 / current_tweak->hud.time_fade; - } - } + cruise_bubble::state_missle_fly::state_missle_fly() : state_type(STATE_MISSLE_FLY) + { + } - S32 i = 0; - while (i < hud.gizmos_used) - { - update_gizmo(hud.gizmo[i], dt); - if (hud.gizmo[i].alpha <= zEntCruiseBubble_f_0_0) - { - hud.gizmos_used -= 1; - hud.gizmo[i] = hud.gizmo[hud.gizmos_used]; - } - else - { - ++i; - } - } + cruise_bubble::state_missle_appear::state_missle_appear() : state_type(STATE_MISSLE_APPEAR) + { + } - for (S32 i = 1; i < hud.gizmos_used; ++i) - { - hud.gizmo[i].flags &= 0xfffffffe; - } + cruise_bubble::state_player_wait::state_player_wait() : state_type(STATE_PLAYER_WAIT) + { } - void show_gizmo(hud_gizmo& gizmo, const basic_rect& rect, xModelInstance* m) + cruise_bubble::state_player_fire::state_player_fire() : state_type(STATE_PLAYER_FIRE) { - gizmo.flags = 0x1; - gizmo.bound = rect; - gizmo.alpha = zEntCruiseBubble_f_0_0; - gizmo.alpha_vel = zEntCruiseBubble_f_1_0 / current_tweak->hud.time_fade; - gizmo.glow = zEntCruiseBubble_f_1_0; - gizmo.glow_vel = zEntCruiseBubble_f_n1_0 / current_tweak->hud.time_glow; - gizmo.opacity = zEntCruiseBubble_f_1_0; - gizmo.target = NULL; - gizmo.model = m; } - void update_gizmo(cruise_bubble::hud_gizmo& gizmo, F32 dt) + cruise_bubble::state_player_aim::state_player_aim() : state_type(STATE_PLAYER_AIM) { - gizmo.alpha = range_limit(gizmo.alpha_vel * dt + gizmo.alpha, zEntCruiseBubble_f_0_0, - zEntCruiseBubble_f_1_0); - gizmo.glow = range_limit(gizmo.glow_vel * dt + gizmo.glow, zEntCruiseBubble_f_0_0, - zEntCruiseBubble_f_1_0); } - void flash_hud() + cruise_bubble::state_player_halt::state_player_halt() : state_type(STATE_PLAYER_HALT) { - // nice meme - hud.glow = zEntCruiseBubble_f_1_0; - hud.glow_vel = zEntCruiseBubble_f_n1_0 / current_tweak->hud.time_glow; } - void render_timer(F32 alpha, F32 glow) + void init_missle_model() { - state_missle_fly* state = (state_missle_fly*)shared.state[THREAD_MISSLE]; - if (state == NULL || state->type != STATE_MISSLE_FLY) + U32 aid = xStrHash("cruise_bubble_bind.MINF"); + xEnt* ent = (xEnt*)xSTFindAsset(aid, NULL); + xModelInstance* model = zEntRecurseModelInfo(ent, NULL); + + model->PipeFlags = model->PipeFlags & 0xffffffcf | 0x10; + model->Data->renderCallBack = &custom_bubble_render; + if (model->Data->renderCallBack == NULL) { - return; + model->Data->renderCallBack = &AtomicDefaultRenderCallBack; } - F32 life = state->life; - char buffer[16]; - sprintf(buffer, "%02d:%02d", (S32)life, - ((S32)(zEntCruiseBubble_f_100_0 * life)) - (100 * (S32)life)); + shared.missle_model = model; + } - F32 dsize = glow * current_tweak->hud.timer.glow_size; - // zEntCruiseBubble_f_0_0 is loaded too early, should be just before the call - xfont font = - xfont::create(current_tweak->hud.timer.font, current_tweak->hud.timer.font_width + dsize, - current_tweak->hud.timer.font_height + dsize, zEntCruiseBubble_f_0_0, g_WHITE, - screen_bounds); - // register use for copying fields into font off, also causes a larger stack frame - // also the color tags are loaded too early, should be just before the call - cruise_bubble::lerp(font.color, glow, zEntCruiseBubble_color_80_00_00_FF, - zEntCruiseBubble_color_FF_14_14_FF); - font.color.a = (S32)(zEntCruiseBubble_f_255_0 * alpha + zEntCruiseBubble_f_0_5); + void reset_wake_ribbons() + { + wake_ribbon[0].set_default_config(); + wake_ribbon[0].cfg.blend_src = 5; + wake_ribbon[0].cfg.blend_dst = 2; - basic_rect bound = font.bounds(buffer); - F32 x = current_tweak->hud.timer.x - bound.x - zEntCruiseBubble_f_0_5 * bound.w; - F32 y = current_tweak->hud.timer.y - bound.y - zEntCruiseBubble_f_0_5 * bound.h; + if (!(shared.flags & 0x200)) + { + wake_ribbon[0].set_texture("lightning"); + wake_ribbon[1].set_texture("lightning"); + wake_ribbon[0].set_curve(&wake_ribbon_curve[0], 2); + wake_ribbon[1].set_curve(&wake_ribbon_curve[0], 2); - font.render(buffer, x, y); + wake_ribbon[0].cfg.life_time = 3.0f; + } + else + { + wake_ribbon[0].set_texture("lightning"); + wake_ribbon[1].set_texture("lightning"); + wake_ribbon[0].set_curve(&cheat_wake_ribbon_curve[0], 2); + wake_ribbon[1].set_curve(&cheat_wake_ribbon_curve[0], 2); + + wake_ribbon[0].cfg.life_time = 3.0f; + } + wake_ribbon[0].cfg.pivot = 1.0f; + wake_ribbon[1].cfg = wake_ribbon[0].cfg; + + wake_ribbon[0].refresh_config(); + wake_ribbon[1].refresh_config(); } - void lerp(iColor_tag& c, F32 t, iColor_tag a, iColor_tag b) + void init_wake_ribbons() { - lerp(c.r, t, a.r, b.r); - lerp(c.g, t, a.g, b.g); - lerp(c.b, t, a.b, b.b); - lerp(c.a, t, a.a, b.a); + wake_ribbon[0].init("Wake Ribbon 0", "Player|Cruise Bubble|Wake Ribbon 0|"); + wake_ribbon[1].init("Wake Ribbon 1", "Player|Cruise Bubble|Wake Ribbon 1|"); + cruise_bubble::reset_wake_ribbons(); } - void load_settings() + void reset_explode_decal() { - U32 params_size; - xModelAssetParam* params = - zEntGetModelParams(xStrHash("cruise_bubble_bind.MINF"), ¶ms_size); + explode_decal.set_default_config(); - if (params == NULL) + explode_decal.cfg.flags = 0x3; + explode_decal.cfg.blend_src = 5; + explode_decal.cfg.blend_dst = 2; + + if (!(shared.flags & 0x200)) { - params_size = 0; + explode_decal.cfg.life_time = 0.5f; + explode_decal.set_curve(&explode_curve[0], 3); + explode_decal.set_texture("par_cruise_explode"); } - - normal_tweak.load(params, params_size); - memcpy(&cheat_tweak, &normal_tweak, sizeof(tweak_group)); - load_cheat_tweak(); - refresh_missle_model(); + else + { + explode_decal.cfg.life_time = 0.5f; + explode_decal.set_curve(&cheat_explode_curve[0], 3); + explode_decal.set_texture("par_cruise_explode"); + } + explode_decal.refresh_config(); } - void lock_target(S32 index, const xVec3* target, F32 opacity) + void init_explode_decal() { - if (index <= -1 && hud.gizmos_used >= 33) - { - return; - } + explode_decal.init(1, "Cruise Bubble Explosion"); + explode_decal.set_default_config(); - hud_gizmo* gizmo; - if (index <= -1) - { - index = hud.gizmos_used; - hud.gizmos_used++; - gizmo = &hud.gizmo[index]; - show_gizmo(hud.gizmo[index], hud.gizmo[index].bound, hud.model.target); - } - gizmo = &hud.gizmo[index]; - xVec3 screen_loc = cruise_bubble::world_to_screen(*target); + // r0 should be use here + explode_decal.cfg.flags = 0x3; + // scheduling off here + explode_decal.cfg.life_time = 0.5f; + explode_decal.cfg.blend_src = 5; + explode_decal.cfg.blend_dst = 2; - gizmo->bound.set_size(current_tweak->hud.target.size); - gizmo->bound.center(screen_loc.x, screen_loc.y); - gizmo->flags = 0x1; - gizmo->alpha_vel = zEntCruiseBubble_f_1_0 / current_tweak->hud.time_fade; - gizmo->model = hud.model.target; - gizmo->target = target; - gizmo->opacity = opacity; + explode_decal.set_curve(explode_curve, 3); + explode_decal.set_texture("par_cruise_explode"); + explode_decal.refresh_config(); } - void update_trail(F32 dt) + void init_shrapnel() { - // will match once file complete - // casting from S32 to float uses a float constant which cannot be extern'd + shared.droplet_shrapnel = + (zShrapnelAsset*)xSTFindAsset(xStrHash("cruise_bubble_droplet_shrapnel"), NULL); + } - if ((shared.flags & 0x80) == 0) + void update_trail(F32 dt) + { + if (!(shared.flags & 0x80)) { return; } @@ -1217,7 +1088,7 @@ namespace cruise_bubble if (samples <= 0) { - shared.trail.samples = zEntCruiseBubble_f_0_0; + shared.trail.samples = 0.0f; samples = 1; } else @@ -1231,7 +1102,7 @@ namespace cruise_bubble cruise_bubble::refresh_trail(end_mat, end_dir); // float cast - F32 ds = zEntCruiseBubble_f_1_0 / (F32)samples; + F32 ds = 1.0f / (F32)samples; F32 ddt = dt * ds; xVec3 dloc = (end_mat.pos - shared.trail.mat.pos) * ds; S32 flip = 0; @@ -1263,91 +1134,26 @@ namespace cruise_bubble { } - void render_hud() + void update_missle(xScene& s, F32 dt) { - if (hud.gizmos_used == 0) + xModelInstance* m = shared.missle_model; + if (!(m->Flags & 2)) { return; } - - zRenderState(SDRS_CruiseHUD); - - if (hud.model.wind->Alpha > 0.0f) - { - RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)5); - RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)2); - - basic_rect bound; - bound.set_size(current_tweak->hud.wind.size, current_tweak->hud.wind.size); - bound.center(0.5f, 0.5f); - - render_model_2d(hud.model.wind, bound, hud.model.wind->Alpha); - } - - RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)5); - RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)6); - - for (S32 i = 0; i < hud.gizmos_used; ++i) - { - hud_gizmo* gizmo = &hud.gizmo[i]; - F32 alpha = hud.alpha * gizmo->alpha * gizmo->opacity; - if (!(alpha <= 0.0f)) - { - render_model_2d(gizmo->model, gizmo->bound, alpha); - } - } - - RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)5); - RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)2); - - for (S32 i = 0; i < hud.gizmos_used; ++i) - { - hud_gizmo* gizmo = &hud.gizmo[i]; - - F32 glow = gizmo->glow + hud.glow; - if (glow > 1.0f) - { - glow = 1.0f; - } - else if (glow <= 0.0f) - { - continue; - } - - F32 alpha = hud.alpha * gizmo->alpha * gizmo->opacity; - if (!(alpha <= 0.0f)) - { - render_glow(gizmo->model, gizmo->bound, glow, alpha); - } - } - - render_timer(hud.alpha, hud.glow); - } - - void show_hud() - { - // scheduling and register usage off - hud.gizmos_used = 1; - basic_rect reticle_bound; - reticle_bound.set_size(current_tweak->hud.reticle.size); - // reticle_bound gets loaded again as r3 here which shouldn't be - // might be a non functional match for edge cases - reticle_bound.center(zEntCruiseBubble_f_0_5, zEntCruiseBubble_f_0_5); - show_gizmo(hud.gizmo[0], reticle_bound, hud.model.reticle); - - hud.model.wind->Alpha = zEntCruiseBubble_f_0_0; - // scheduling off for this float - hud.alpha = zEntCruiseBubble_f_0_0; - hud.alpha_vel = zEntCruiseBubble_f_1_0 / current_tweak->hud.time_fade; - - flash_hud(); + xModelUpdate(m, dt); + xModelEval(m); + update_trail(dt); } - void hide_hud() + void render_missle() { - hud.gizmos_used = 0; - // float scheduling ... - hud.model.wind->Alpha = zEntCruiseBubble_f_0_0; + xModelInstance* m = shared.missle_model; + if (!(m->Flags & 1)) + { + return; + } + xModelRender(m); } // return type guessed based on return type of zEntRecurseModelInfo and xModelInstanceAlloc @@ -1375,15 +1181,35 @@ namespace cruise_bubble return xModelInstanceAlloc((RpAtomic*)model, NULL, 0, 0, NULL); } + void render_model_2d(xModelInstance* model, const basic_rect& rect, F32 param_3) + { + xVec3 r = { 0.0f, 0.0f, 1.0f }; + xVec3 from = { 0.0f, 0.0f, -0.01f }; + + xMat4x3 frame; + + frame.right.assign(1.01f, 0.0f, 0.0f); + frame.up.assign(0.0f, 1.01f, 0.0f); + frame.at.assign(0.0f, 0.0f, 0.01f); + frame.pos = 0.0f; + + for (; model != NULL; model = model->Next) + { + xModelSetMaterialAlpha(model, (S32)((param_3 * 255.0f) + 0.5f) & 0xFF); + xModelSetFrame(model, &frame); + xModelRender2D(*model, rect, r, from); + } + } + void init_hud() { // should use stbu here and save an addi instruction // which should also fix the rest of the function by correcting offsets hud.hiding = false; - hud.alpha = zEntCruiseBubble_f_0_0; - hud.alpha_vel = zEntCruiseBubble_f_0_0; - hud.glow = zEntCruiseBubble_f_0_0; + hud.alpha = 0.0f; + hud.alpha_vel = 0.0f; + hud.glow = 0.0f; hud.gizmos_used = 0; hud.model.reticle = load_model(xStrHash("ui_3dicon_reticle")); @@ -1394,2292 +1220,2504 @@ namespace cruise_bubble hud.uv_wind.offset_vel.assign(current_tweak->hud.wind.du, current_tweak->hud.wind.dv); } - void render_debug() + bool uv_animated_model::init(RpAtomic* m) { - // empty + this->model = m; + if (m == NULL) + { + return false; + } + + if (!this->clone_uv(this->uv, this->uvsize, m)) + { + return false; + } + + this->offset.assign(0.0f, 0.0f); + this->offset_vel.assign(0.0f, 0.0f); + return true; } - void render_missle() + bool uv_animated_model::clone_uv(RwTexCoords*& coords, S32& size, RpAtomic* m) const { - xModelInstance* m = shared.missle_model; - if ((m->Flags & 1) == 0) + RwTexCoords* c; + if (!this->get_uv(c, size, m)) { - return; + return false; } - xModelRender(m); + + coords = (RwTexCoords*)xMemAlloc(gActiveHeap, size * 8, 0); + if (coords == NULL) + { + return false; + } + + memcpy(coords, c, size * 8); // [memcpy] + return true; } - void reset_explode_decal() + bool uv_animated_model::get_uv(RwTexCoords*& coords, S32& size, RpAtomic* m) const { - explode_decal.set_default_config(); - - explode_decal.cfg.flags = 0x3; - explode_decal.cfg.blend_src = 5; - explode_decal.cfg.blend_dst = 2; + coords = NULL; + size = 0; - if ((shared.flags & 0x200) == 0) + RpGeometry* geo = m->geometry; + if (geo == NULL) { - explode_decal.cfg.life_time = zEntCruiseBubble_f_0_5; - explode_decal.set_curve(&explode_curve[0], 3); - explode_decal.set_texture("par_cruise_explode"); + return false; } - else + + size = geo->numVertices; + if (!(size > 0)) { - explode_decal.cfg.life_time = zEntCruiseBubble_f_0_5; - explode_decal.set_curve(&cheat_explode_curve[0], 3); - explode_decal.set_texture("par_cruise_explode"); + return false; } - explode_decal.refresh_config(); + + coords = *geo->texCoords; + return coords != NULL; } - void init_explode_decal() + void show_gizmo(hud_gizmo& gizmo, const basic_rect& rect, xModelInstance* m) { - explode_decal.init(1, "Cruise Bubble Explosion"); - explode_decal.set_default_config(); - - // r0 should be use here - explode_decal.cfg.flags = 0x3; - // scheduling off here - explode_decal.cfg.life_time = zEntCruiseBubble_f_0_5; - explode_decal.cfg.blend_src = 5; - explode_decal.cfg.blend_dst = 2; + gizmo.flags = 0x1; + gizmo.bound = rect; + gizmo.alpha = 0.0f; + gizmo.alpha_vel = 1.0f / current_tweak->hud.time_fade; + gizmo.glow = 1.0f; + gizmo.glow_vel = -1.0f / current_tweak->hud.time_glow; + gizmo.opacity = 1.0f; + gizmo.target = NULL; + gizmo.model = m; + } - explode_decal.set_curve(explode_curve, 3); - explode_decal.set_texture("par_cruise_explosion"); - explode_decal.refresh_config(); + void update_gizmo(cruise_bubble::hud_gizmo& gizmo, F32 dt) + { + gizmo.alpha = range_limit(gizmo.alpha_vel * dt + gizmo.alpha, 0.0f, + 1.0f); + gizmo.glow = range_limit(gizmo.glow_vel * dt + gizmo.glow, 0.0f, + 1.0f); } - void set_pitch(S32 which, F32 pitch, U32 handle) + void flash_hud() { - sound_config* s = &sounds[which]; + // nice meme + hud.glow = 1.0f; + hud.glow_vel = -1.0f / current_tweak->hud.time_glow; + } - if (s->id == 0) + void render_timer(F32 alpha, F32 glow) + { + state_missle_fly* state = (state_missle_fly*)shared.state[THREAD_MISSLE]; + if (state == NULL || state->type != STATE_MISSLE_FLY) { - for (S32 i = s->first; i <= s->last; ++i) - { - zEntPlayer_SNDSetPitch((_tagePlayerSnd)i, pitch); - } return; } - if (handle == 0) - { - handle = s->handle; - } + F32 life = state->life; + char buffer[16]; + sprintf(buffer, "%02d:%02d", (S32)life, + ((S32)(100.0f * life)) - (100 * (S32)life)); - if (handle != 0) - { - xSndSetPitch(handle, pitch); - } + F32 dsize = glow * current_tweak->hud.timer.glow_size; + // zEntCruiseBubble_f_0_0 is loaded too early, should be just before the call + xfont font = + xfont::create(current_tweak->hud.timer.font, current_tweak->hud.timer.font_width + dsize, + current_tweak->hud.timer.font_height + dsize, 0.0f, g_WHITE, + screen_bounds); + // register use for copying fields into font off, also causes a larger stack frame + // also the color tags are loaded too early, should be just before the call + cruise_bubble::lerp(font.color, glow, zEntCruiseBubble_color_80_00_00_FF, + zEntCruiseBubble_color_FF_14_14_FF); + font.color.a = (S32)(255.0f * alpha + 0.5f); + + basic_rect bound = font.bounds(buffer); + F32 x = current_tweak->hud.timer.x - bound.x - 0.5f * bound.w; + F32 y = current_tweak->hud.timer.y - bound.y - 0.5f * bound.h; + + font.render(buffer, x, y); } - void update_missle(xScene& s, F32 dt) + void lerp(iColor_tag& c, F32 t, iColor_tag a, iColor_tag b) { - xModelInstance* m = shared.missle_model; - if ((m->Flags & 2) == 0) - { - return; - } - xModelUpdate(m, dt); - xModelEval(m); - update_trail(dt); + lerp(c.r, t, a.r, b.r); + lerp(c.g, t, a.g, b.g); + lerp(c.b, t, a.b, b.b); + lerp(c.a, t, a.a, b.a); } - xVec3 world_to_screen(const xVec3& loc) + void lerp(U8& x, F32 t, U8 a, U8 b) { - iCameraUpdatePos(globals.camera.lo_cam, &globals.camera.mat); + x = 0.5f + ((F32)a + t * ((F32)b - (F32)a)); + } - xVec3 world_loc; - xMat4x3* view_mat = (xMat4x3*)&globals.camera.lo_cam->viewMatrix; - xMat4x3Toworld(&world_loc, view_mat, &loc); + S32 cruise_bubble::state_camera_survey::find_nearest(F32 radius) const + { + S32 j; + S32 i = 0; + S32 size = missle_record.size(); + S32 range = size; - xVec3 screen_loc; - F32 iz = zEntCruiseBubble_f_1_0 / world_loc.z; - screen_loc.assign(world_loc.x * iz, world_loc.y * iz, zEntCruiseBubble_f_1_0); + while (i < range) + { + j = (i + range) / 2; - return screen_loc; + if (radius < path_distance[j]) + { + size = range; + } + if (radius <= path_distance[j]) + { + range = j; + } + else if (radius > path_distance[j]) + { + i = j + 1; + } + } + + return i; } - void damage_entity(xEnt& ent, const xVec3& loc, const xVec3& dir, const xVec3& hit_norm, - F32 radius, bool explosive) + void update_hud(F32 dt) { - if (shared.hits_size >= 32) + if (hud.gizmos_used == 0) { return; } - shared.hits[shared.hits_size] = &ent; - shared.hits_size++; - - switch (ent.baseType) - { - case eBaseTypeButton: - zEntButton_Press((_zEntButton*)&ent, 0x10); - return; - case eBaseTypeDestructObj: - zEntDestructObj_Hit((zEntDestructObj*)&ent, 0x10000); - return; + hud.alpha = range_limit(hud.alpha_vel * dt + hud.alpha, 0.0f, + 1.0f); + hud.glow = range_limit(hud.glow_vel * dt + hud.glow, 0.0f, + 1.0f); - case eBaseTypePlatform: - switch (ent.subType) - { - case 0xc: - if ((((zPlatform*)&ent)->passet->paddle.paddleFlags & 0x20) == 0) - { - return; - } + // scheduling off + F32 vel_frac = ((state_missle_fly*)shared.states[STATE_MISSLE_FLY])->vel / + current_tweak->missle.fly.max_vel; - xCollis coll; - coll.optr = &ent; - coll.mptr = ent.collModel != NULL ? ent.collModel : ent.model; + hud.uv_wind.offset_vel.assign(current_tweak->hud.wind.du, current_tweak->hud.wind.dv); + hud.uv_wind.offset_vel *= vel_frac; + hud.model.wind->Alpha = vel_frac; + hud.uv_wind.update(dt); - if (explosive != 0) - { - coll.flags = 0x600; + // sheduling off for i and zEntCruiseBubble_f_n1_0 + for (S32 i = 1; i < hud.gizmos_used; ++i) + { + if (!(hud.gizmo[i].flags & 0x1)) + { + hud.gizmo[i].alpha_vel = -1.0f / current_tweak->hud.time_fade; + } + } - xSphere o; - o.center = loc; - o.r = radius; - xSphereHitsBound(&o, &ent.bound, &coll); + S32 i = 0; + while (i < hud.gizmos_used) + { + update_gizmo(hud.gizmo[i], dt); + if (hud.gizmo[i].alpha <= 0.0f) + { + hud.gizmos_used -= 1; + hud.gizmo[i] = hud.gizmo[hud.gizmos_used]; + } + else + { + ++i; + } + } - if ((coll.flags & 0x1) == 0) - { - return; - } + for (S32 i = 1; i < hud.gizmos_used; ++i) + { + hud.gizmo[i].flags &= 0xfffffffe; + } + } - if (ent.collLev == 0x5) - { - xSphereHitsModel(&o, coll.mptr, &coll); + void uv_animated_model::update(F32 dt) + { + if (0.0f == this->offset_vel.x && + 0.0f == this->offset_vel.y) + { + return; + } - if ((coll.flags & 0x1) == 0) - { - return; - } - } + this->offset += this->offset_vel * dt; + this->offset.x = xfmod(this->offset.x, 1.0f); + this->offset.y = xfmod(this->offset.y, 1.0f); + this->refresh(); + } - xVec3 hit_dir = coll.tohit.up_normal(); - zPlatform_PaddleCollide(&coll, &loc, &hit_dir, 0x1); - return; - } + void render_hud() + { + if (hud.gizmos_used == 0) + { + return; + } - coll.flags = 0x201; - coll.norm = hit_norm; - zPlatform_PaddleCollide(&coll, &loc, &dir, 1); - return; + zRenderState(SDRS_CruiseHUD); + + if (hud.model.wind->Alpha > 0.0f) + { + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)5); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)2); + + basic_rect bound; + bound.set_size(current_tweak->hud.wind.size, current_tweak->hud.wind.size); + bound.center(0.5f, 0.5f); + + render_model_2d(hud.model.wind, bound, hud.model.wind->Alpha); + } + + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)5); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)6); + + for (S32 i = 0; i < hud.gizmos_used; ++i) + { + hud_gizmo* gizmo = &hud.gizmo[i]; + F32 alpha = hud.alpha * gizmo->alpha * gizmo->opacity; + if (!(alpha <= 0.0f)) + { + render_model_2d(gizmo->model, gizmo->bound, alpha); } - break; + } - case eBaseTypeNPC: - if (explosive) + RwRenderStateSet(rwRENDERSTATESRCBLEND, (void*)5); + RwRenderStateSet(rwRENDERSTATEDESTBLEND, (void*)2); + + for (S32 i = 0; i < hud.gizmos_used; ++i) + { + hud_gizmo* gizmo = &hud.gizmo[i]; + + F32 glow = gizmo->glow + hud.glow; + if (glow > 1.0f) { - // fuck this... weird scheduling - xVec3 edir = (*xEntGetCenter(&ent) - loc).up_normal(); - ((zNPCCommon*)&ent)->Damage(DMGTYP_CRUISEBUBBLE, &base, &edir); + glow = 1.0f; } - else + else if (glow <= 0.0f) { - // while this matches - ((zNPCCommon*)&ent)->Damage(DMGTYP_CRUISEBUBBLE, &base, &dir); + continue; + } + + F32 alpha = hud.alpha * gizmo->alpha * gizmo->opacity; + if (!(alpha <= 0.0f)) + { + render_glow(gizmo->model, gizmo->bound, glow, alpha); } - return; } - zEntEvent(&ent, 0x1c7); + render_timer(hud.alpha, hud.glow); } - void init_missle_model() + + void show_hud() { - U32 aid = xStrHash("cruise_bubble_bind.MINF"); - xEnt* ent = (xEnt*)xSTFindAsset(aid, NULL); - xModelInstance* model = zEntRecurseModelInfo(ent, NULL); + // scheduling and register usage off + hud.gizmos_used = 1; + basic_rect reticle_bound; + reticle_bound.set_size(current_tweak->hud.reticle.size); + // reticle_bound gets loaded again as r3 here which shouldn't be + // might be a non functional match for edge cases + reticle_bound.center(0.5f, 0.5f); + show_gizmo(hud.gizmo[0], reticle_bound, hud.model.reticle); - model->PipeFlags = model->PipeFlags & 0xffffffcf | 0x10; - model->Data->renderCallBack = &custom_bubble_render; - if (model->Data->renderCallBack == NULL) + hud.model.wind->Alpha = 0.0f; + // scheduling off for this float + hud.alpha = 0.0f; + hud.alpha_vel = 1.0f / current_tweak->hud.time_fade; + + flash_hud(); + } + + void hide_hud() + { + hud.gizmos_used = 0; + // float scheduling ... + hud.model.wind->Alpha = 0.0f; + } + + xVec3 world_to_screen(const xVec3& loc) + { + iCameraUpdatePos(globals.camera.lo_cam, &globals.camera.mat); + + xVec3 world_loc; + xMat4x3* view_mat = (xMat4x3*)&globals.camera.lo_cam->viewMatrix; + xMat4x3Toworld(&world_loc, view_mat, &loc); + + xVec3 screen_loc; + F32 iz = 1.0f / world_loc.z; + screen_loc.assign(world_loc.x * iz, world_loc.y * iz, 1.0f); + + return screen_loc; + } + + S32 find_locked_target(const xVec3* target) + { + for (S32 i = 1; i < hud.gizmos_used; ++i) { - model->Data->renderCallBack = &AtomicDefaultRenderCallBack; + if (hud.gizmo[i].target == target) + { + return i; + } } - shared.missle_model = model; + return -1; } - void reset_wake_ribbons() + void lock_target(S32 index, const xVec3* target, F32 opacity) { - wake_ribbon[0].set_default_config(); - wake_ribbon[0].cfg.blend_src = 5; - wake_ribbon[0].cfg.blend_dst = 2; + if (index <= -1 && hud.gizmos_used >= 33) + { + return; + } - if ((shared.flags & 0x200) == 0) + hud_gizmo* gizmo; + if (index <= -1) { - wake_ribbon[0].set_texture("lightning"); - wake_ribbon[1].set_texture("lightning"); - wake_ribbon[0].set_curve(&wake_ribbon_curve[0], 2); - wake_ribbon[1].set_curve(&wake_ribbon_curve[0], 2); + index = hud.gizmos_used; + hud.gizmos_used++; + gizmo = &hud.gizmo[index]; + show_gizmo(hud.gizmo[index], hud.gizmo[index].bound, hud.model.target); + } + gizmo = &hud.gizmo[index]; + xVec3 screen_loc = cruise_bubble::world_to_screen(*target); + + gizmo->bound.set_size(current_tweak->hud.target.size); + gizmo->bound.center(screen_loc.x, screen_loc.y); + gizmo->flags = 0x1; + gizmo->alpha_vel = 1.0f / current_tweak->hud.time_fade; + gizmo->model = hud.model.target; + gizmo->target = target; + gizmo->opacity = opacity; + } + + void check_lock_target(const xVec3* target) + { + xMat4x3* mat = &globals.camera.mat; + xVec3 offset = *target - mat->pos; + F32 ang = offset.dot(mat->at); - wake_ribbon[0].cfg.life_time = zEntCruiseBubble_f_3_0; + if (ang < current_tweak->reticle.dist_min || ang > current_tweak->reticle.dist_max) + { + return; + } + + ang = offset.length(); + if ((ang >= -0.00001f) && (ang <= 0.00001f)) + { + ang = 0.0f; } else { - wake_ribbon[0].set_texture("lightning"); - wake_ribbon[1].set_texture("lightning"); - wake_ribbon[0].set_curve(&cheat_wake_ribbon_curve[0], 2); - wake_ribbon[1].set_curve(&cheat_wake_ribbon_curve[0], 2); + ang = xacos(offset.dot(mat->at) / ang); + } + + F32 max_ang = current_tweak->reticle.ang_show; + F32 min_ang = current_tweak->reticle.ang_hide; - wake_ribbon[0].cfg.life_time = zEntCruiseBubble_f_3_0; + if (ang >= min_ang) + { + return; } - wake_ribbon[0].cfg.pivot = zEntCruiseBubble_f_1_0; - wake_ribbon[1].cfg = wake_ribbon[0].cfg; - wake_ribbon[0].refresh_config(); - wake_ribbon[1].refresh_config(); + if (ang >= max_ang) + { + S32 t = find_locked_target(target); + if (t < 0) + { + return; + } + + lock_target(t, target, (min_ang - ang) / (min_ang - max_ang)); + } + + else + { + lock_target(find_locked_target(target), target, 1.0f); + } } - } // namespace -} // namespace cruise_bubble + U32 check_anim_aim(xAnimTransition*, xAnimSingle*, void*) + { + return false; + } -void cruise_bubble::state_type::start() -{ - // empty -} + // Very dumb scheduling. + void load_cheat_tweak() + { + *(volatile F32*)(&cheat_tweak.missle.crash_angle) = 0.7536f; + *(volatile F32*)(&cheat_tweak.missle.collide_twist) = 0.05f; + *(volatile F32*)(&cheat_tweak.missle.appear.delay_fly) = 0.16666667f; + *(volatile F32*)(&cheat_tweak.missle.fly.accel) = 12.0f; + *(volatile F32*)(&cheat_tweak.missle.fly.turn.xdelta) = 7.0f; + *(volatile F32*)(&cheat_tweak.missle.fly.turn.ydelta) = 5.0f; + *(volatile F32*)(&cheat_tweak.missle.fly.turn.ydecay) = + *(volatile F32*)(&cheat_tweak.missle.fly.turn.xdecay) = 0.985f; + *(volatile F32*)(&cheat_tweak.missle.fly.turn.ybound) = 1.24248f; + + F32 one_tenth = 0.1f; + *(volatile F32*)(&cheat_tweak.missle.fly.turn.roll_frac) = one_tenth; + *(volatile F32*)(&cheat_tweak.missle.explode.hit_radius) = 2.0f; + *(volatile F32*)(&cheat_tweak.camera.seize.blend_time) = 0.75f; + *(volatile F32*)(&cheat_tweak.camera.survey.duration) = 1.0f; + *(volatile F32*)(&cheat_tweak.camera.survey.min_duration) = one_tenth; + + *(volatile F32*)(&cheat_tweak.camera.survey.drift_dist) = 10.0f; + *(volatile F32*)(&cheat_tweak.material.env_alpha) = 0.2f; + *(volatile U32*)(&cheat_tweak.material.env_texture) = xStrHash("aura2"); + *(volatile F32*)(&cheat_tweak.material.fresnel_alpha) = 0.1f; + *(volatile F32*)(&cheat_tweak.material.fresnel_coeff) = 1.0f; + *(volatile U32*)(&cheat_tweak.material.fresnel_texture) = xStrHash("par_cruise_explode"); + *(volatile F32*)(&cheat_tweak.trail.bubble_rate) = 90.0f; + *(volatile F32*)(&cheat_tweak.trail.bubble_emit_radius) = 0.75f; + *(volatile F32*)(&cheat_tweak.trail.wake_emit_radius) = 0.3f; + *(volatile U32*)(&cheat_tweak.blast.emit) = 400; + *(volatile F32*)(&cheat_tweak.blast.vel) = 7.5f; + *(volatile U32*)(&cheat_tweak.droplet.emit_min) = 0xf; + *(volatile F32*)(&cheat_tweak.droplet.vel_min) = 4.0f; + *(volatile F32*)(&cheat_tweak.droplet.vel_max) = 8.0f; + } -void cruise_bubble::state_type::stop() -{ - // empty -} + void load_settings() + { + U32 params_size; + xModelAssetParam* params = + zEntGetModelParams(xStrHash("cruise_bubble_bind.MINF"), ¶ms_size); -void cruise_bubble::state_type::abort() -{ - // empty -} + if (params == NULL) + { + params_size = 0; + } -void cruise_bubble::state_type::render() -{ - // empty -} + normal_tweak.load(params, params_size); + memcpy(&cheat_tweak, &normal_tweak, sizeof(tweak_group)); + load_cheat_tweak(); + refresh_missle_model(); + } -cruise_bubble::state_camera_restore::state_camera_restore() : state_type(STATE_CAMERA_RESTORE) -{ -} + void tweak_group::load(xModelAssetParam* params, U32 size) + { + this->register_tweaks(true, params, size, NULL); + } -cruise_bubble::state_type::state_type(cruise_bubble::state_enum type) -{ - this->type = type; -} + void tweak_group::register_tweaks(bool init, xModelAssetParam* ap, U32 apsize, + const char*) + { + if (init) + { + this->aim_delay = 0.2f; + auto_tweak::load_param(this->aim_delay, 1.0f, 0.0f, 1.0f, ap, + apsize, "aim_delay"); + } -cruise_bubble::state_camera_survey::state_camera_survey() : state_type(STATE_CAMERA_SURVEY) -{ -} + if (init) + { + this->player.halt_time = 0.5f; + auto_tweak::load_param(this->player.halt_time, 1.0f, + 0.0f, 1.0f, ap, apsize, + "player.halt_time"); + } -cruise_bubble::state_camera_attach::state_camera_attach() : state_type(STATE_CAMERA_ATTACH) -{ -} + if (init) + { + this->player.aim.turn_speed = 0.05f; + auto_tweak::load_param(this->player.aim.turn_speed, 1.0f, + 0.01f, 1000000000.0f, + ap, apsize, "player.aim.turn_speed"); + } -cruise_bubble::state_camera_seize::state_camera_seize() : state_type(STATE_CAMERA_SEIZE) -{ -} + if (init) + { + this->player.aim.anim_delta = 0.5f; + auto_tweak::load_param(this->player.aim.anim_delta, 1.0f, + 0.0f, 1000000000.0f, + ap, apsize, "player.aim.anim_delta"); + } -cruise_bubble::state_camera_aim::state_camera_aim() : state_type(STATE_CAMERA_AIM) -{ -} + if (init) + { + this->player.fire.delay_wand = 0.06666667f; + auto_tweak::load_param(this->player.fire.delay_wand, 1.0f, + 0.0f, 100.0f, ap, + apsize, "player.fire.delay_wand"); + } -cruise_bubble::state_missle_explode::state_missle_explode() : state_type(STATE_MISSLE_EXPLODE) -{ -} + if (init) + { + this->missle.life = 6.0f; + auto_tweak::load_param(this->missle.life, 1.0f, + 0.01f, 1000000000.0f, + ap, apsize, "missle.life"); + } -cruise_bubble::state_missle_fly::state_missle_fly() : state_type(STATE_MISSLE_FLY) -{ -} + if (init) + { + this->missle.hit_dist = 0.3f; + auto_tweak::load_param(this->missle.hit_dist, 1.0f, + 0.0f, 1.0f, ap, apsize, + "missle.hit_dist"); + } -cruise_bubble::state_missle_appear::state_missle_appear() : state_type(STATE_MISSLE_APPEAR) -{ -} + if (init) + { + this->missle.crash_angle = 30.0f; + auto_tweak::load_param(this->missle.crash_angle, DEG2RAD(1), + 0.0f, 60.0f, ap, + apsize, "missle.crash_angle"); + } -cruise_bubble::state_player_wait::state_player_wait() : state_type(STATE_PLAYER_WAIT) -{ -} + if (init) + { + this->missle.collide_twist = 0.025f; + auto_tweak::load_param(this->missle.collide_twist, 1.0f, + 0.0f, 1.0f, ap, apsize, + "missle.collide_twist"); + } -cruise_bubble::state_player_fire::state_player_fire() : state_type(STATE_PLAYER_FIRE) -{ -} + if (init) + { + this->missle.hit_tests = 4; + auto_tweak::load_param(this->missle.hit_tests, 1, 1, 100, ap, apsize, + "missle.hit_tests"); + } -cruise_bubble::state_player_aim::state_player_aim() : state_type(STATE_PLAYER_AIM) -{ -} + if (init) + { + this->missle.appear.delay_show = 0.13333334f; + auto_tweak::load_param(this->missle.appear.delay_show, 1.0f, + 0.0f, 100.0f, ap, + apsize, "missle.appear.delay_show"); + } -cruise_bubble::state_player_halt::state_player_halt() : state_type(STATE_PLAYER_HALT) -{ -} + if (init) + { + this->missle.appear.delay_fly = 0.6666667f; + auto_tweak::load_param(this->missle.appear.delay_fly, 1.0f, + 0.0f, 100.0f, ap, + apsize, "missle.appear.delay_fly"); + } -bool cruise_bubble::uv_animated_model::init(RpAtomic* m) -{ - this->model = m; - if (m == NULL) - { - return false; - } + if (init) + { + this->missle.appear.offset = xVec3::create(-0.049f, 1.728f, 0.922f); + auto_tweak::load_param(this->missle.appear.offset, 0, 0, 0, ap, apsize, + "missle.appear.offset"); + } - if (!this->clone_uv(this->uv, this->uvsize, m)) - { - return false; - } + if (init) + { + this->missle.fly.accel = 6.0f; + auto_tweak::load_param(this->missle.fly.accel, 1.0f, + 0.01f, 1000000000.0f, + ap, apsize, "missle.fly.accel"); + } - this->offset.assign(zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_0_0); - this->offset_vel.assign(zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_0_0); - return true; -} + if (init) + { + this->missle.fly.max_vel = 12.0f; + auto_tweak::load_param(this->missle.fly.max_vel, 1.0f, + 0.01f, 1000000000.0f, + ap, apsize, "missle.fly.max_vel"); + } -bool cruise_bubble::uv_animated_model::clone_uv(RwTexCoords*& coords, S32& size, RpAtomic* m) const -{ - RwTexCoords* c; - if (!this->get_uv(c, size, m)) - { - return false; - } + if (init) + { + this->missle.fly.engine_pitch_max = 10.0f; + auto_tweak::load_param(this->missle.fly.engine_pitch_max, 1.0f, + 0.01f, 1000000000.0f, + ap, apsize, "missle.fly.engine_pitch_max"); + } - coords = (RwTexCoords*)xMemAlloc(gActiveHeap, size * 8, 0); - if (coords == NULL) - { - return false; - } + if (init) + { + this->missle.fly.engine_pitch_sensitivity = 0.005f; + auto_tweak::load_param(this->missle.fly.engine_pitch_sensitivity, + 1.0f, 0.0f, + 1.0f, ap, apsize, + "missle.fly.engine_pitch_sensitivity"); + } - memcpy(coords, c, size * 8); // [memcpy] - return true; -} + if (init) + { + this->missle.fly.flash_interval = 2.0f; + auto_tweak::load_param(this->missle.fly.flash_interval, 1.0f, + 0.0f, 1000000000.0f, + ap, apsize, "missle.fly.flash_interval"); + } + + if (init) + { + this->missle.fly.turn.xdelta = 5.0f; + auto_tweak::load_param(this->missle.fly.turn.xdelta, 1.0f, + 0.01f, 1000000000.0f, + ap, apsize, "missle.fly.turn.xdelta"); + } + + if (init) + { + this->missle.fly.turn.ydelta = 4.0f; + auto_tweak::load_param(this->missle.fly.turn.ydelta, 1.0f, + 0.01f, 1000000000.0f, + ap, apsize, "missle.fly.turn.ydelta"); + } + + if (init) + { + this->missle.fly.turn.xdecay = 0.99f; + auto_tweak::load_param(this->missle.fly.turn.xdecay, 1.0f, + 0.0f, 1.0f, ap, apsize, + "missle.fly.turn.xdecay"); + } + + if (init) + { + this->missle.fly.turn.ydecay = 0.99f; + auto_tweak::load_param(this->missle.fly.turn.ydecay, 1.0f, + 0.0f, 1.0f, ap, apsize, + "missle.fly.turn.ydecay"); + } + + if (init) + { + this->missle.fly.turn.ybound = 0.6f; + auto_tweak::load_param(this->missle.fly.turn.ybound, PI / 2, + 0.0f, 1.0f, ap, apsize, + "missle.fly.turn.ybound"); + } + + if (init) + { + this->missle.fly.turn.roll_frac = 0.2f; + auto_tweak::load_param(this->missle.fly.turn.roll_frac, 1.0f, + -1.0f, 1.0f, ap, + apsize, "missle.fly.turn.roll_frac"); + } + + if (init) + { + this->missle.explode.hit_radius = 1.0f; + auto_tweak::load_param(this->missle.explode.hit_radius, 1.0f, + 0.0f, 10.0f, ap, + apsize, "missle.explode.hit_radius"); + } + + if (init) + { + this->missle.explode.hit_duration = 0.25f; + auto_tweak::load_param(this->missle.explode.hit_duration, 1.0f, + 0.0f, 10.0f, ap, + apsize, "missle.explode.hit_duration"); + } + + if (init) + { + this->camera.aim.dist = 2.0f; + auto_tweak::load_param(this->camera.aim.dist, 1.0f, + 0.0f, 100.0f, ap, + apsize, "camera.aim.dist"); + } + + if (init) + { + this->camera.aim.height = 1.5f; + auto_tweak::load_param(this->camera.aim.height, 1.0f, + -10.0f, 10.0f, ap, + apsize, "camera.aim.height"); + } + + if (init) + { + this->camera.aim.pitch = 0.0f; + auto_tweak::load_param(this->camera.aim.pitch, DEG2RAD(1), + -90.0f, 90.0f, ap, + apsize, "camera.aim.pitch"); + } + + if (init) + { + this->camera.aim.accel = 10.0f; + auto_tweak::load_param(this->camera.aim.accel, 1.0f, + 0.01f, 1000000000.0f, + ap, apsize, "camera.aim.accel"); + } -bool cruise_bubble::uv_animated_model::get_uv(RwTexCoords*& coords, S32& size, RpAtomic* m) const -{ - coords = NULL; - size = 0; + if (init) + { + this->camera.aim.max_vel = 5.0f; + auto_tweak::load_param(this->camera.aim.max_vel, 1.0f, + 0.01f, 1000000000.0f, + ap, apsize, "camera.aim.max_vel"); + } - RpGeometry* geo = m->geometry; - if (geo == 0) - { - return false; - } + if (init) + { + this->camera.aim.stick_decel = 720.0f; + auto_tweak::load_param(this->camera.aim.stick_decel, DEG2RAD(1), + 0.01f, 1000000000.0f, + ap, apsize, "camera.aim.stick_decel"); + } - size = geo->numVertices; - if (!(size > 0)) - { - return false; - } + if (init) + { + this->camera.aim.stick_accel = 360.0f; + auto_tweak::load_param(this->camera.aim.stick_accel, DEG2RAD(1), + 0.01f, 1000000000.0f, + ap, apsize, "camera.aim.stick_accel"); + } - coords = *geo->texCoords; - return coords != NULL; -} + if (init) + { + this->camera.aim.stick_max_vel = 135.0f; + auto_tweak::load_param(this->camera.aim.stick_max_vel, DEG2RAD(1), + 0.01f, 1000000000.0f, + ap, apsize, "camera.aim.stick_max_vel"); + } -void cruise_bubble::uv_animated_model::update(F32 dt) -{ - if (zEntCruiseBubble_f_0_0 == this->offset_vel.x && - zEntCruiseBubble_f_0_0 == this->offset_vel.y) - { - return; - } + if (init) + { + this->camera.aim.turn_speed = 0.2f; + auto_tweak::load_param(this->camera.aim.turn_speed, 1.0f, + 0.001f, 1000000000.0f, + ap, apsize, "camera.aim.turn_speed"); + } - this->offset += this->offset_vel * dt; - this->offset.x = xfmod(this->offset.x, zEntCruiseBubble_f_1_0); - this->offset.y = xfmod(this->offset.y, zEntCruiseBubble_f_1_0); - this->refresh(); -} + if (init) + { + this->camera.seize.delay = 0.0f; + auto_tweak::load_param(this->camera.seize.delay, 1.0f, + 0.0f, 1000000000.0f, + ap, apsize, "camera.seize.delay"); + } -void cruise_bubble::tweak_group::load(xModelAssetParam* params, U32 size) -{ - this->register_tweaks(true, params, size, NULL); -} + if (init) + { + this->camera.seize.blend_time = 1.5f; + auto_tweak::load_param(this->camera.seize.blend_time, 1.0f, + 0.0f, 1000000000.0f, + ap, apsize, "camera.seize.blend_time"); + } -void cruise_bubble::tweak_group::register_tweaks(bool init, xModelAssetParam* ap, U32 apsize, - const char*) -{ - if (init) - { - this->aim_delay; - this->aim_delay = zEntCruiseBubble_f_0_2; - auto_tweak::load_param(this->aim_delay, 1.0f, zEntCruiseBubble_f_0_0, 1.0f, ap, - apsize, "aim_delay"); - } + if (init) + { + this->camera.seize.fade_dist = 2.0f; + auto_tweak::load_param(this->camera.seize.fade_dist, 1.0f, + 0.0f, 10.0f, ap, + apsize, "camera.seize.fade_dist"); + } - if (init) - { - this->player.halt_time = zEntCruiseBubble_f_0_5; - auto_tweak::load_param(this->player.halt_time, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_1_0, ap, apsize, - "player.halt_time"); - } + if (init) + { + this->camera.seize.hide_dist = 1.0f; + auto_tweak::load_param(this->camera.seize.hide_dist, 1.0f, + 0.0f, 10.0f, ap, + apsize, "camera.seize.hide_dist"); + } - if (init) - { - this->player.aim.turn_speed = zEntCruiseBubble_f_0_05; - auto_tweak::load_param(this->player.aim.turn_speed, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_01, zEntCruiseBubble_f_1000000000_0, - ap, apsize, "player.aim.turn_speed"); - } + if (init) + { + this->camera.seize.fov = 95.0f; + auto_tweak::load_param(this->camera.seize.fov, 1.0f, + 10.0f, 180.0f, ap, + apsize, "camera.seize.fov"); + } - if (init) - { - this->player.aim.anim_delta = zEntCruiseBubble_f_0_5; - auto_tweak::load_param(this->player.aim.anim_delta, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_1000000000_0, - ap, apsize, "player.aim.anim_delta"); - } + if (init) + { + this->camera.survey.duration = 2.0f; + auto_tweak::load_param(this->camera.survey.duration, 1.0f, + 0.0f, 10.0f, ap, + apsize, "camera.survey.duration"); + } - if (init) - { - this->player.fire.delay_wand = zEntCruiseBubble_f_0_0667; - auto_tweak::load_param(this->player.fire.delay_wand, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_100_0, ap, - apsize, "player.fire.delay_wand"); - } + if (init) + { + this->camera.survey.min_duration = 0.25f; + auto_tweak::load_param(this->camera.survey.min_duration, 1.0f, + 0.0f, 10.0f, ap, + apsize, "camera.survey.min_duration"); + } - if (init) - { - this->missle.life = zEntCruiseBubble_f_6_0; - auto_tweak::load_param(this->missle.life, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_01, zEntCruiseBubble_f_1000000000_0, - ap, apsize, "missle.life"); - } + if (init) + { + this->camera.survey.min_dist = 10.0f; + auto_tweak::load_param(this->camera.survey.min_dist, 1.0f, + 0.0f, 100.0f, ap, + apsize, "camera.survey.min_dist"); + } - if (init) - { - this->missle.hit_dist = zEntCruiseBubble_f_0_3; - auto_tweak::load_param(this->missle.hit_dist, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_1_0, ap, apsize, - "missle.hit_dist"); - } + if (init) + { + this->camera.survey.cut_dist = 6.0f; + auto_tweak::load_param(this->camera.survey.cut_dist, 1.0f, + 0.0f, 100.0f, ap, + apsize, "camera.survey.cut_dist"); + } - if (init) - { - this->missle.crash_angle = zEntCruiseBubble_f_30_0; - auto_tweak::load_param(this->missle.crash_angle, zEntCruiseBubble_f_0_017, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_60_0, ap, - apsize, "missle.crash_angle"); - } + if (init) + { + this->camera.survey.drift_dist = 8.0f; + auto_tweak::load_param(this->camera.survey.drift_dist, 1.0f, + 0.0f, 100.0f, ap, + apsize, "camera.survey.drift_dist"); + } - if (init) - { - this->missle.collide_twist = zEntCruiseBubble_f_0_025; - auto_tweak::load_param(this->missle.collide_twist, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_1_0, ap, apsize, - "missle.collide_twist"); - } + if (init) + { + this->camera.survey.drift_softness = 0.1f; + auto_tweak::load_param(this->camera.survey.drift_softness, 1.0f, + 0.0f, 0.5f, ap, apsize, + "camera.survey.drift_softness"); + } - if (init) - { - this->missle.hit_tests = 4; - auto_tweak::load_param(this->missle.hit_tests, 1, 1, 100, ap, apsize, - "missle.hit_tests"); - } + if (init) + { + this->camera.survey.jerk_offset = 0.8f; + auto_tweak::load_param(this->camera.survey.jerk_offset, 1.0f, + 0.0f, 2.0f, ap, apsize, + "camera.survey.jerk_offset"); + } - if (init) - { - this->missle.appear.delay_show = zEntCruiseBubble_f_0_1333; - auto_tweak::load_param(this->missle.appear.delay_show, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_100_0, ap, - apsize, "missle.appear.delay_show"); - } + if (init) + { + this->camera.survey.jerk_deflect = 0.6f; + auto_tweak::load_param(this->camera.survey.jerk_deflect, 1.0f, + 0.0f, 1.0f, ap, apsize, + "camera.survey.jerk_deflect"); + } - if (init) - { - this->missle.appear.delay_fly = zEntCruiseBubble_f_0_667; - auto_tweak::load_param(this->missle.appear.delay_fly, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_100_0, ap, - apsize, "missle.appear.delay_fly"); - } + if (init) + { + this->camera.restore.control_delay = 0.25f; + auto_tweak::load_param(this->camera.restore.control_delay, 1.0f, + 0.0f, 1.0f, ap, apsize, + "camera.restore.control_delay"); + } - if (init) - { - this->missle.appear.offset = xVec3::create( - zEntCruiseBubble_f_n0_049, zEntCruiseBubble_f_1_728, zEntCruiseBubble_f_0_922); - auto_tweak::load_param(this->missle.appear.offset, 0, 0, 0, ap, apsize, - "missle.appear.offset"); - } + if (init) + { + this->material.env_alpha = 0.5f; + auto_tweak::load_param(this->material.env_alpha, 1.0f, + 0.0f, 1.0f, ap, apsize, + "material.env_alpha"); + } - if (init) - { - this->missle.fly.accel = zEntCruiseBubble_f_6_0; - auto_tweak::load_param(this->missle.fly.accel, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_01, zEntCruiseBubble_f_1000000000_0, - ap, apsize, "missle.fly.accel"); - } + if (init) + { + this->material.env_coeff = 0.5f; + auto_tweak::load_param(this->material.env_coeff, 1.0f, + 0.0f, 1.0f, ap, apsize, + "material.env_coeff"); + } - if (init) - { - this->missle.fly.max_vel = zEntCruiseBubble_f_12_0; - auto_tweak::load_param(this->missle.fly.max_vel, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_01, zEntCruiseBubble_f_1000000000_0, - ap, apsize, "missle.fly.max_vel"); - } + if (init) + { + this->material.fresnel_alpha = 0.0f; + auto_tweak::load_param(this->material.fresnel_alpha, 1.0f, + 0.0f, 1.0f, ap, apsize, + "material.fresnel_alpha"); + } - if (init) - { - this->missle.fly.engine_pitch_max = zEntCruiseBubble_f_10_0; - auto_tweak::load_param(this->missle.fly.engine_pitch_max, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_01, zEntCruiseBubble_f_1000000000_0, - ap, apsize, "missle.fly.engine_pitch_max"); - } + if (init) + { + this->material.fresnel_coeff = 0.75f; + auto_tweak::load_param(this->material.fresnel_coeff, 1.0f, + 0.0f, 1.0f, ap, apsize, + "material.fresnel_coeff"); + } - if (init) - { - this->missle.fly.engine_pitch_sensitivity = zEntCruiseBubble_f_0_005; - auto_tweak::load_param(this->missle.fly.engine_pitch_sensitivity, - zEntCruiseBubble_f_1_0, zEntCruiseBubble_f_0_0, - zEntCruiseBubble_f_1_0, ap, apsize, - "missle.fly.engine_pitch_sensitivity"); - } + if (init) + { + this->reticle.dist_min = 3.0f; + auto_tweak::load_param(this->reticle.dist_min, 1.0f, + 1.0f, 10.0f, ap, + apsize, "reticle.dist_min"); + } - if (init) - { - this->missle.fly.flash_interval = zEntCruiseBubble_f_2_0; - auto_tweak::load_param(this->missle.fly.flash_interval, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_1000000000_0, - ap, apsize, "missle.fly.flash_interval"); - } + if (init) + { + this->reticle.dist_max = 30.0f; + auto_tweak::load_param(this->reticle.dist_max, 1.0f, + 1.0f, 10000.0f, ap, + apsize, "reticle.dist_max"); + } - if (init) - { - this->missle.fly.turn.xdelta = zEntCruiseBubble_f_5_0; - auto_tweak::load_param(this->missle.fly.turn.xdelta, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_01, zEntCruiseBubble_f_1000000000_0, - ap, apsize, "missle.fly.turn.xdelta"); - } + if (init) + { + this->reticle.ang_show = 4.0f; + auto_tweak::load_param(this->reticle.ang_show, DEG2RAD(1), + 0.0f, 90.0f, ap, + apsize, "reticle.ang_show"); + } - if (init) - { - this->missle.fly.turn.ydelta = zEntCruiseBubble_f_4_0; - auto_tweak::load_param(this->missle.fly.turn.ydelta, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_01, zEntCruiseBubble_f_1000000000_0, - ap, apsize, "missle.fly.turn.ydelta"); - } + if (init) + { + this->reticle.ang_hide = 22.5f; + auto_tweak::load_param(this->reticle.ang_hide, DEG2RAD(1), + 0.0f, 90.0f, ap, + apsize, "reticle.ang_hide"); + } - if (init) - { - this->missle.fly.turn.xdecay = zEntCruiseBubble_f_0_99; - auto_tweak::load_param(this->missle.fly.turn.xdecay, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_1_0, ap, apsize, - "missle.fly.turn.xdecay"); - } + if (init) + { + this->reticle.delay_retarget = 0.25f; + auto_tweak::load_param(this->reticle.delay_retarget, 1.0f, + 0.0f, 5.0f, ap, apsize, + "reticle.delay_retarget"); + } - if (init) - { - this->missle.fly.turn.ydecay = zEntCruiseBubble_f_0_99; - auto_tweak::load_param(this->missle.fly.turn.ydecay, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_1_0, ap, apsize, - "missle.fly.turn.ydecay"); - } + if (init) + { + this->trail.sample_rate = 60.0f; + auto_tweak::load_param(this->trail.sample_rate, 1.0f, + 0.0f, 10000.0f, ap, + apsize, "trail.sample_rate"); + } - if (init) - { - this->missle.fly.turn.ybound = zEntCruiseBubble_f_0_6; - auto_tweak::load_param(this->missle.fly.turn.ybound, zEntCruiseBubble_f_1_5708, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_1_0, ap, apsize, - "missle.fly.turn.ybound"); - } + if (init) + { + this->trail.bubble_rate = 60.0f; + auto_tweak::load_param(this->trail.bubble_rate, 1.0f, + 0.0f, 10000.0f, ap, + apsize, "trail.bubble_rate"); + } - if (init) - { - this->missle.fly.turn.roll_frac = zEntCruiseBubble_f_0_2; - auto_tweak::load_param(this->missle.fly.turn.roll_frac, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_n1_0, zEntCruiseBubble_f_1_0, ap, - apsize, "missle.fly.turn.roll_frac"); - } + if (init) + { + this->trail.bubble_emit_radius = 0.5f; + auto_tweak::load_param(this->trail.bubble_emit_radius, 1.0f, + 0.0f, 10.0f, ap, + apsize, "trail.bubble_emit_radius"); + } - if (init) - { - this->missle.explode.hit_radius = zEntCruiseBubble_f_1_0; - auto_tweak::load_param(this->missle.explode.hit_radius, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_10_0, ap, - apsize, "missle.explode.hit_radius"); - } + if (init) + { + this->trail.wake_emit_radius = 0.1f; + auto_tweak::load_param(this->trail.wake_emit_radius, 1.0f, + 0.0f, 10.0f, ap, + apsize, "trail.wake_emit_radius"); + } - if (init) - { - this->missle.explode.hit_duration = zEntCruiseBubble_f_0_25; - auto_tweak::load_param(this->missle.explode.hit_duration, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_10_0, ap, - apsize, "missle.explode.hit_duration"); - } + if (init) + { + this->blast.emit = 300; + auto_tweak::load_param(this->blast.emit, 1, 0, 0x3e8, ap, apsize, "blast.emit"); + } - if (init) - { - this->camera.aim.dist = zEntCruiseBubble_f_2_0; - auto_tweak::load_param(this->camera.aim.dist, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_100_0, ap, - apsize, "camera.aim.dist"); - } + if (init) + { + this->blast.radius = 0.0f; + auto_tweak::load_param(this->blast.radius, 1.0f, + 0.0f, 10.0f, ap, + apsize, "blast.radius"); + } - if (init) - { - this->camera.aim.height = zEntCruiseBubble_f_1_5; - auto_tweak::load_param(this->camera.aim.height, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_n10_0, zEntCruiseBubble_f_10_0, ap, - apsize, "camera.aim.height"); - } + if (init) + { + this->blast.vel = 5.0f; + auto_tweak::load_param(this->blast.vel, 1.0f, + -100000.0f, 100000.0f, + ap, apsize, "blast.vel"); + } - if (init) - { - this->camera.aim.pitch = zEntCruiseBubble_f_0_0; - auto_tweak::load_param(this->camera.aim.pitch, zEntCruiseBubble_f_0_017, - zEntCruiseBubble_f_n90_0, zEntCruiseBubble_f_90_0, ap, - apsize, "camera.aim.pitch"); - } + if (init) + { + this->blast.rand_vel = 0.5f; + auto_tweak::load_param(this->blast.rand_vel, 1.0f, + -100000.0f, 100000.0f, + ap, apsize, "blast.rand_vel"); + } - if (init) - { - this->camera.aim.accel = zEntCruiseBubble_f_10_0; - auto_tweak::load_param(this->camera.aim.accel, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_01, zEntCruiseBubble_f_1000000000_0, - ap, apsize, "camera.aim.accel"); - } + if (init) + { + this->droplet.dist_min = 1.0f; + auto_tweak::load_param(this->droplet.dist_min, 1.0f, + 0.0f, 10.0f, ap, + apsize, "droplet.dist_min"); + } - if (init) - { - this->camera.aim.max_vel = zEntCruiseBubble_f_5_0; - auto_tweak::load_param(this->camera.aim.max_vel, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_01, zEntCruiseBubble_f_1000000000_0, - ap, apsize, "camera.aim.max_vel"); - } + if (init) + { + this->droplet.dist_max = 2.0f; + auto_tweak::load_param(this->droplet.dist_max, 1.0f, + 0.0f, 10.0f, ap, + apsize, "droplet.dist_max"); + } - if (init) - { - this->camera.aim.stick_decel = zEntCruiseBubble_f_720_0; - auto_tweak::load_param(this->camera.aim.stick_decel, zEntCruiseBubble_f_0_017, - zEntCruiseBubble_f_0_01, zEntCruiseBubble_f_1000000000_0, - ap, apsize, "camera.aim.stick_decel"); - } + if (init) + { + this->droplet.emit_min = 10; + auto_tweak::load_param(this->droplet.emit_min, 1, 0, 0x1e, ap, apsize, + "droplet.emit_min"); + } - if (init) - { - this->camera.aim.stick_accel = zEntCruiseBubble_f_360_0; - auto_tweak::load_param(this->camera.aim.stick_accel, zEntCruiseBubble_f_0_017, - zEntCruiseBubble_f_0_01, zEntCruiseBubble_f_1000000000_0, - ap, apsize, "camera.aim.stick_accel"); - } + if (init) + { + this->droplet.emit_max = 20; + auto_tweak::load_param(this->droplet.emit_max, 1, 0, 0x1e, ap, apsize, + "droplet.emit_max"); + } - if (init) - { - this->camera.aim.stick_max_vel = zEntCruiseBubble_f_135_0; - auto_tweak::load_param(this->camera.aim.stick_max_vel, zEntCruiseBubble_f_0_017, - zEntCruiseBubble_f_0_01, zEntCruiseBubble_f_1000000000_0, - ap, apsize, "camera.aim.stick_max_vel"); - } + if (init) + { + this->droplet.vel_min = 2.0f; + auto_tweak::load_param(this->droplet.vel_min, 1.0f, + 0.0f, 100000.0f, ap, + apsize, "droplet.vel_min"); + } - if (init) - { - this->camera.aim.turn_speed = zEntCruiseBubble_f_0_2; - auto_tweak::load_param(this->camera.aim.turn_speed, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_001, zEntCruiseBubble_f_1000000000_0, - ap, apsize, "camera.aim.turn_speed"); - } + if (init) + { + this->droplet.vel_max = 6.0f; + auto_tweak::load_param(this->droplet.vel_max, 1.0f, + 0.0f, 100000.0f, ap, + apsize, "droplet.vel_max"); + } - if (init) - { - this->camera.seize.delay = zEntCruiseBubble_f_0_0; - auto_tweak::load_param(this->camera.seize.delay, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_1000000000_0, - ap, apsize, "camera.seize.delay"); - } + if (init) + { + this->droplet.vel_perturb = 0.25f; + auto_tweak::load_param(this->droplet.vel_perturb, 1.0f, + 0.0f, 100000.0f, ap, + apsize, "droplet.vel_perturb"); + } - if (init) - { - this->camera.seize.blend_time = zEntCruiseBubble_f_1_5; - auto_tweak::load_param(this->camera.seize.blend_time, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_1000000000_0, - ap, apsize, "camera.seize.blend_time"); - } + if (init) + { + this->droplet.vel_angle = 60.0f; + auto_tweak::load_param(this->droplet.vel_angle, DEG2RAD(1), + 0.0f, 100000.0f, ap, + apsize, "droplet.vel_angle"); + } - if (init) - { - this->camera.seize.fade_dist = zEntCruiseBubble_f_2_0; - auto_tweak::load_param(this->camera.seize.fade_dist, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_10_0, ap, - apsize, "camera.seize.fade_dist"); - } + if (init) + { + this->droplet.rot_vel_max = 360.0f; + auto_tweak::load_param(this->droplet.rot_vel_max, DEG2RAD(1), + 0.0f, 100000.0f, ap, + apsize, "droplet.rot_vel_max"); + } - if (init) - { - this->camera.seize.hide_dist = zEntCruiseBubble_f_1_0; - auto_tweak::load_param(this->camera.seize.hide_dist, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_10_0, ap, - apsize, "camera.seize.hide_dist"); - } + if (init) + { + this->hud.glow_size = 0.05f; + auto_tweak::load_param(this->hud.glow_size, 1.0f, + 0.001f, 10.0f, ap, + apsize, "hud.glow_size"); + } - if (init) - { - this->camera.seize.fov = zEntCruiseBubble_f_95_0; - auto_tweak::load_param(this->camera.seize.fov, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_10_0, zEntCruiseBubble_f_180_0, ap, - apsize, "camera.seize.fov"); - } + if (init) + { + this->hud.time_fade = 0.25f; + auto_tweak::load_param(this->hud.time_fade, 1.0f, + 0.001f, 10.0f, ap, + apsize, "hud.time_fade"); + } - if (init) - { - this->camera.survey.duration = zEntCruiseBubble_f_2_0; - auto_tweak::load_param(this->camera.survey.duration, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_10_0, ap, - apsize, "camera.survey.duration"); - } + if (init) + { + this->hud.time_glow = 0.5f; + auto_tweak::load_param(this->hud.time_glow, 1.0f, + 0.001f, 10.0f, ap, + apsize, "hud.time_glow"); + } - if (init) - { - this->camera.survey.min_duration = zEntCruiseBubble_f_0_25; - auto_tweak::load_param(this->camera.survey.min_duration, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_10_0, ap, - apsize, "camera.survey.min_duration"); - } + if (init) + { + this->hud.wind.size = 0.75f; + auto_tweak::load_param(this->hud.wind.size, 1.0f, + 0.0f, 100.0f, ap, + apsize, "hud.wind.size"); + } - if (init) - { - this->camera.survey.min_dist = zEntCruiseBubble_f_10_0; - auto_tweak::load_param(this->camera.survey.min_dist, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_100_0, ap, - apsize, "camera.survey.min_dist"); - } + if (init) + { + this->hud.wind.du = 0.0f; + auto_tweak::load_param(this->hud.wind.du, 1.0f, + -1000000000.0f, + 1000000000.0f, ap, apsize, + "hud.wind.du"); + } - if (init) - { - this->camera.survey.cut_dist = zEntCruiseBubble_f_6_0; - auto_tweak::load_param(this->camera.survey.cut_dist, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_100_0, ap, - apsize, "camera.survey.cut_dist"); - } + if (init) + { + this->hud.wind.dv = 4.0f; + auto_tweak::load_param(this->hud.wind.dv, 1.0f, + -1000000000.0f, + 1000000000.0f, ap, apsize, + "hud.wind.dv"); + } - if (init) - { - this->camera.survey.drift_dist = zEntCruiseBubble_f_8_0; - auto_tweak::load_param(this->camera.survey.drift_dist, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_100_0, ap, - apsize, "camera.survey.drift_dist"); - } + if (init) + { + this->hud.reticle.size = 0.1f; + auto_tweak::load_param(this->hud.reticle.size, 1.0f, + 0.0f, 100.0f, ap, + apsize, "hud.reticle.size"); + } - if (init) - { - this->camera.survey.drift_softness = zEntCruiseBubble_f_0_1; - auto_tweak::load_param(this->camera.survey.drift_softness, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_0_5, ap, apsize, - "camera.survey.drift_softness"); - } + if (init) + { + this->hud.target.size = 0.1f; + auto_tweak::load_param(this->hud.target.size, 1.0f, + 0.0f, 100.0f, ap, + apsize, "hud.target.size"); + } - if (init) - { - this->camera.survey.jerk_offset = zEntCruiseBubble_f_0_8; - auto_tweak::load_param(this->camera.survey.jerk_offset, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_2_0, ap, apsize, - "camera.survey.jerk_offset"); - } + if (init) + { + this->hud.timer.font = 2; + auto_tweak::load_param(this->hud.timer.font, 1, 0, 4, ap, apsize, + "hud.timer.font"); + } - if (init) - { - this->camera.survey.jerk_deflect = zEntCruiseBubble_f_0_6; - auto_tweak::load_param(this->camera.survey.jerk_deflect, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_1_0, ap, apsize, - "camera.survey.jerk_deflect"); - } + if (init) + { + this->hud.timer.font_width = 0.0275f; + auto_tweak::load_param(this->hud.timer.font_width, 1.0f, + 0.001f, 1.0f, ap, + apsize, "hud.timer.font_width"); + } - if (init) - { - this->camera.restore.control_delay = zEntCruiseBubble_f_0_25; - auto_tweak::load_param(this->camera.restore.control_delay, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_1_0, ap, apsize, - "camera.restore.control_delay"); - } + if (init) + { + this->hud.timer.font_height = 0.047f; + auto_tweak::load_param(this->hud.timer.font_height, 1.0f, + 0.001f, 1.0f, ap, + apsize, "hud.timer.font_height"); + } - if (init) - { - this->material.env_alpha = zEntCruiseBubble_f_0_5; - auto_tweak::load_param(this->material.env_alpha, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_1_0, ap, apsize, - "material.env_alpha"); - } + if (init) + { + this->hud.timer.x = 0.78f; + auto_tweak::load_param(this->hud.timer.x, 1.0f, + 0.0f, 1.0f, ap, apsize, + "hud.timer.x"); + } - if (init) - { - this->material.env_coeff = zEntCruiseBubble_f_0_5; - auto_tweak::load_param(this->material.env_coeff, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_1_0, ap, apsize, - "material.env_coeff"); - } + if (init) + { + this->hud.timer.y = 0.86f; + auto_tweak::load_param(this->hud.timer.y, 1.0f, + 0.0f, 1.0f, ap, apsize, + "hud.timer.y"); + } - if (init) - { - this->material.fresnel_alpha = zEntCruiseBubble_f_0_0; - auto_tweak::load_param(this->material.fresnel_alpha, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_1_0, ap, apsize, - "material.fresnel_alpha"); - } + if (init) + { + this->hud.timer.glow_size = 0.01f; + auto_tweak::load_param(this->hud.timer.glow_size, 1.0f, + 0.0f, 10.0f, ap, + apsize, "hud.timer.glow_size"); + } - if (init) - { - this->material.fresnel_coeff = zEntCruiseBubble_f_0_75; - auto_tweak::load_param(this->material.fresnel_coeff, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_1_0, ap, apsize, - "material.fresnel_coeff"); - } + if (init) + { + this->dialog.freq = 1.0f; + auto_tweak::load_param(this->dialog.freq, 1.0f, + 0.0f, 1.0f, ap, apsize, + "dialog.freq"); + } - if (init) - { - this->reticle.dist_min = zEntCruiseBubble_f_3_0; - auto_tweak::load_param(this->reticle.dist_min, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_1_0, zEntCruiseBubble_f_10_0, ap, - apsize, "reticle.dist_min"); - } + if (init) + { + this->dialog.decay = 0.75f; + auto_tweak::load_param(this->dialog.decay, 1.0f, + 0.0f, 1.0f, ap, apsize, + "dialog.decay"); + } - if (init) - { - this->reticle.dist_max = zEntCruiseBubble_f_30_0; - auto_tweak::load_param(this->reticle.dist_max, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_1_0, zEntCruiseBubble_f_10000_0, ap, - apsize, "reticle.dist_max"); - } + if (init) + { + this->dialog.min_freq = 0.1f; + auto_tweak::load_param(this->dialog.min_freq, 1.0f, + 0.0f, 1.0f, ap, apsize, + "dialog.min_freq"); + } - if (init) - { - this->reticle.ang_show = zEntCruiseBubble_f_4_0; - auto_tweak::load_param(this->reticle.ang_show, zEntCruiseBubble_f_0_017, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_90_0, ap, - apsize, "reticle.ang_show"); - } + if (init) + { + this->material.fresnel_texture = xStrHash("gloss_edge"); + this->material.env_texture = xStrHash("rainbowfilm_smooth32"); + } + } - if (init) - { - this->reticle.ang_hide = zEntCruiseBubble_f_22_5; - auto_tweak::load_param(this->reticle.ang_hide, zEntCruiseBubble_f_0_017, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_90_0, ap, - apsize, "reticle.ang_hide"); - } + void cruise_bubble::init() + { + if ((shared.flags & 0x1) != 0x1) + { + return; + } - if (init) - { - this->reticle.delay_retarget = zEntCruiseBubble_f_0_25; - auto_tweak::load_param(this->reticle.delay_retarget, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_5_0, ap, apsize, - "reticle.delay_retarget"); - } + cruise_bubble::init_sound(); - if (init) - { - this->trail.sample_rate = zEntCruiseBubble_f_60_0; - auto_tweak::load_param(this->trail.sample_rate, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_10000_0, ap, - apsize, "trail.sample_rate"); - } + shared.flags |= 0x2; - if (init) - { - this->trail.bubble_rate = zEntCruiseBubble_f_60_0; - auto_tweak::load_param(this->trail.bubble_rate, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_10000_0, ap, - apsize, "trail.bubble_rate"); - } + cruise_bubble::load_settings(); + cruise_bubble::init_states(); + cruise_bubble::init_missle_model(); + cruise_bubble::init_wake_ribbons(); + cruise_bubble::init_explode_decal(); + cruise_bubble::init_shrapnel(); + cruise_bubble::init_hud(); + cruise_bubble::init_debug(); - if (init) - { - this->trail.bubble_emit_radius = zEntCruiseBubble_f_0_5; - auto_tweak::load_param(this->trail.bubble_emit_radius, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_10_0, ap, - apsize, "trail.bubble_emit_radius"); - } + // scheduling off + shared.fov_default = xCameraGetFOV(&globals.camera); + shared.dialog_freq = current_tweak->dialog.freq; + } - if (init) - { - this->trail.wake_emit_radius = zEntCruiseBubble_f_0_1; - auto_tweak::load_param(this->trail.wake_emit_radius, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_10_0, ap, - apsize, "trail.wake_emit_radius"); - } + void init_debug() + { + // empty + } - if (init) - { - this->blast.emit = 300; - auto_tweak::load_param(this->blast.emit, 1, 0, 0x3e8, ap, apsize, "blast.emit"); - } + void cruise_bubble::reset() + { + if ((shared.flags & 0x3) == 0x3) + { + cruise_bubble::kill(true, false); + } + } - if (init) - { - this->blast.radius = zEntCruiseBubble_f_0_0; - auto_tweak::load_param(this->blast.radius, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_10_0, ap, - apsize, "blast.radius"); - } + void cruise_bubble::launch() + { + if ((shared.flags & 0x13) != 0x3) + { + return; + } - if (init) - { - this->blast.vel = zEntCruiseBubble_f_5_0; - auto_tweak::load_param(this->blast.vel, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_n100000_0, zEntCruiseBubble_f_100000_0, - ap, apsize, "blast.vel"); - } + if (zGameExtras_CheatFlags() & 0x20000000) + { + // scheduling off + shared.flags |= 0x200; + current_tweak = &cheat_tweak; + } + else + { + current_tweak = &normal_tweak; + } - if (init) - { - this->blast.rand_vel = zEntCruiseBubble_f_0_5; - auto_tweak::load_param(this->blast.rand_vel, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_n100000_0, zEntCruiseBubble_f_100000_0, - ap, apsize, "blast.rand_vel"); - } + cruise_bubble::reset_wake_ribbons(); + cruise_bubble::reset_explode_decal(); - if (init) - { - this->droplet.dist_min = zEntCruiseBubble_f_1_0; - auto_tweak::load_param(this->droplet.dist_min, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_10_0, ap, - apsize, "droplet.dist_min"); - } + shared.flags = shared.flags | 0x14; + shared.last_sp = shared.sp = globals.pad0->analog[0].offset; + shared.player_health = globals.player.Health; + // scheduling off + shared.player_motion = 0.0f; + shared.fov_default = xCameraGetFOV(&globals.camera); - if (init) - { - this->droplet.dist_max = zEntCruiseBubble_f_2_0; - auto_tweak::load_param(this->droplet.dist_max, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_10_0, ap, - apsize, "droplet.dist_max"); - } + ztalkbox::permit(0x0, 0xffffffff); + cruise_bubble::set_state(THREAD_PLAYER, BEGIN_STATE_PLAYER); + } - if (init) - { - this->droplet.emit_min = 10; - auto_tweak::load_param(this->droplet.emit_min, 1, 0, 0x1e, ap, apsize, - "droplet.emit_min"); - } + bool cruise_bubble::update(xScene* s, F32 dt) + { + if ((shared.flags & 0x3) != 0x3) + { + return false; + } - if (init) - { - this->droplet.emit_max = 20; - auto_tweak::load_param(this->droplet.emit_max, 1, 0, 0x1e, ap, apsize, - "droplet.emit_max"); - } + if (!(shared.flags & 0x10)) + { + if (cruise_bubble::check_launch()) + { + launch(); + } + else + { + return false; + } + } - if (init) - { - this->droplet.vel_min = zEntCruiseBubble_f_2_0; - auto_tweak::load_param(this->droplet.vel_min, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_100000_0, ap, - apsize, "droplet.vel_min"); - } + if (globals.player.ControlOff) + { + cruise_bubble::kill(true, false); + return false; + } - if (init) - { - this->droplet.vel_max = zEntCruiseBubble_f_6_0; - auto_tweak::load_param(this->droplet.vel_max, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_100000_0, ap, - apsize, "droplet.vel_max"); - } + cruise_bubble::refresh_controls(); + cruise_bubble::update_state(s, dt); - if (init) - { - this->droplet.vel_perturb = zEntCruiseBubble_f_0_25; - auto_tweak::load_param(this->droplet.vel_perturb, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_100000_0, ap, - apsize, "droplet.vel_perturb"); - } + if (!(shared.flags & 0x10)) + { + return false; + } - if (init) - { - this->droplet.vel_angle = zEntCruiseBubble_f_60_0; - auto_tweak::load_param(this->droplet.vel_angle, zEntCruiseBubble_f_0_017, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_100000_0, ap, - apsize, "droplet.vel_angle"); - } + cruise_bubble::update_player(*s, dt); + cruise_bubble::update_missle(*s, dt); + cruise_bubble::update_hud(dt); + return true; + } - if (init) - { - this->droplet.rot_vel_max = zEntCruiseBubble_f_360_0; - auto_tweak::load_param(this->droplet.rot_vel_max, zEntCruiseBubble_f_0_017, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_100000_0, ap, - apsize, "droplet.rot_vel_max"); - } + bool cruise_bubble::render() + { + if ((shared.flags & 0x7) != 0x7) + { + return false; + } - if (init) - { - this->hud.glow_size = zEntCruiseBubble_f_0_05; - auto_tweak::load_param(this->hud.glow_size, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_001, zEntCruiseBubble_f_10_0, ap, - apsize, "hud.glow_size"); - } + cruise_bubble::render_state(); + cruise_bubble::render_player(); + cruise_bubble::render_missle(); + cruise_bubble::render_debug(); - if (init) - { - this->hud.time_fade = zEntCruiseBubble_f_0_25; - auto_tweak::load_param(this->hud.time_fade, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_001, zEntCruiseBubble_f_10_0, ap, - apsize, "hud.time_fade"); - } + return true; + } - if (init) - { - this->hud.time_glow = zEntCruiseBubble_f_0_5; - auto_tweak::load_param(this->hud.time_glow, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_001, zEntCruiseBubble_f_10_0, ap, - apsize, "hud.time_glow"); - } + void render_debug() + { + // empty + } - if (init) - { - this->hud.wind.size = zEntCruiseBubble_f_0_75; - auto_tweak::load_param(this->hud.wind.size, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_100_0, ap, - apsize, "hud.wind.size"); - } + void cruise_bubble::render_screen() + { + if ((shared.flags & 0x7) == 0x7) + { + cruise_bubble::render_hud(); + } + } - if (init) - { - this->hud.wind.du = zEntCruiseBubble_f_0_0; - auto_tweak::load_param(this->hud.wind.du, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_n1000000000_0, - zEntCruiseBubble_f_1000000000_0, ap, apsize, - "hud.wind.du"); - } + void cruise_bubble::insert_player_animations(xAnimTable& table) + { + if (shared.astate.player.aim != NULL) + { + return; + } - if (init) - { - this->hud.wind.dv = zEntCruiseBubble_f_4_0; - auto_tweak::load_param(this->hud.wind.dv, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_n1000000000_0, - zEntCruiseBubble_f_1000000000_0, ap, apsize, - "hud.wind.dv"); - } + shared.astate.player.aim = + xAnimTableNewState(&table, "cruise_bubble_aim", 0x10, 0, 1.0f, NULL, NULL, + 0.0f, NULL, NULL, xAnimDefaultBeforeEnter, NULL, NULL); - if (init) - { - this->hud.reticle.size = zEntCruiseBubble_f_0_1; - auto_tweak::load_param(this->hud.reticle.size, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_100_0, ap, - apsize, "hud.reticle.size"); - } + shared.astate.player.fire = + xAnimTableNewState(&table, "cruise_bubble_fire", 0x20, 0, 1.0f, NULL, + NULL, 0.0f, NULL, NULL, xAnimDefaultBeforeEnter, NULL, + NULL); - if (init) - { - this->hud.target.size = zEntCruiseBubble_f_0_1; - auto_tweak::load_param(this->hud.target.size, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_100_0, ap, - apsize, "hud.target.size"); - } + shared.astate.player.idle = + xAnimTableNewState(&table, "cruise_bubble_idle", 0x10, 0, 1.0f, NULL, + NULL, 0.0f, NULL, NULL, xAnimDefaultBeforeEnter, NULL, + NULL); - if (init) - { - this->hud.timer.font = 2; - auto_tweak::load_param(this->hud.timer.font, 1, 0, 4, ap, apsize, - "hud.timer.font"); - } + char* start_from = (char*)xMemPushTemp(0x250); + memset(start_from, 0, 0x250); + char* s = start_from; + *s = '\0'; + for (U32 i = 0; i < 37; ++i) + { + strcat(s, start_anim_states[i]); + s += strlen(s); + *s = ' '; + *++s = '\0'; + } - if (init) - { - this->hud.timer.font_width = zEntCruiseBubble_f_0_0275; - auto_tweak::load_param(this->hud.timer.font_width, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_001, zEntCruiseBubble_f_1_0, ap, - apsize, "hud.timer.font_width"); - } + shared.atran.player.aim = + xAnimTableNewTransition(&table, start_from, "cruise_bubble_aim", + (xAnimTransitionConditionalCallback)&check_anim_aim, NULL, 0, 0, + 0.0f, 0.0f, 0, 0, + 0.15f, NULL); - if (init) - { - this->hud.timer.font_height = zEntCruiseBubble_f_0_047; - auto_tweak::load_param(this->hud.timer.font_height, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_001, zEntCruiseBubble_f_1_0, ap, - apsize, "hud.timer.font_height"); - } + shared.atran.player.fire = + xAnimTableNewTransition(&table, "cruise_bubble_aim", "cruise_bubble_fire", NULL, NULL, 0, 0, + 0.0f, 0.0f, 0, 0, + 0.15f, NULL); - if (init) - { - this->hud.timer.x = zEntCruiseBubble_f_0_78; - auto_tweak::load_param(this->hud.timer.x, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_1_0, ap, apsize, - "hud.timer.x"); - } + shared.atran.player.idle = + xAnimTableNewTransition(&table, "cruise_bubble_fire", "cruise_bubble_idle", NULL, NULL, + 0x10, 0, 0.0f, 0.0f, 0, 0, + 0.15f, NULL); - if (init) - { - this->hud.timer.y = zEntCruiseBubble_f_0_86; - auto_tweak::load_param(this->hud.timer.y, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_1_0, ap, apsize, - "hud.timer.y"); - } + shared.atran.player.end = + xAnimTableNewTransition(&table, "cruise_bubble_aim cruise_bubble_fire cruise_bubble_idle", + "Idle01", NULL, NULL, 0, 0, 0.0f, + 0.0f, 0, 0, 0.15f, NULL); - if (init) - { - this->hud.timer.glow_size = zEntCruiseBubble_f_0_01; - auto_tweak::load_param(this->hud.timer.glow_size, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_10_0, ap, - apsize, "hud.timer.glow_size"); - } + xMemPopTemp(start_from); + } - if (init) - { - this->dialog.freq = zEntCruiseBubble_f_1_0; - auto_tweak::load_param(this->dialog.freq, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_1_0, ap, apsize, - "dialog.freq"); - } + xAnimTable* cruise_bubble::anim_table() + { + xAnimTable* table = xAnimTableNew("Cruise Bubble", 0, 0); + shared.astate.missle.fire = xAnimTableNewState(table, "fire", 0x20, 0, 1.0f, NULL, NULL, 0.0f, NULL, NULL, xAnimDefaultBeforeEnter, NULL, NULL); + shared.astate.missle.fly = xAnimTableNewState(table, "fly", 0x10, 0, 1.0f, NULL, NULL, 0.0f, NULL, NULL, xAnimDefaultBeforeEnter, NULL, NULL); + shared.atran.missle.fly = xAnimTableNewTransition(table, "fire", "fly", NULL, NULL, 0x10, 0, 0.0f, 0.0f, 0, 0, 0.15f, NULL); + return table; + } - if (init) - { - this->dialog.decay = zEntCruiseBubble_f_0_75; - auto_tweak::load_param(this->dialog.decay, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_1_0, ap, apsize, - "dialog.decay"); - } + bool cruise_bubble::active() + { + return shared.flags & 0x10; + } - if (init) - { - this->dialog.min_freq = zEntCruiseBubble_f_0_1; - auto_tweak::load_param(this->dialog.min_freq, zEntCruiseBubble_f_1_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_1_0, ap, apsize, - "dialog.min_freq"); - } + F32 cruise_bubble::exploding() + { + state_missle_explode* state = (state_missle_explode*)shared.state[THREAD_MISSLE]; + if (state == NULL || state->type != STATE_MISSLE_EXPLODE) + { + return 0.0f; + } - if (init) - { - this->material.fresnel_texture = xStrHash("gloss_edge"); - this->material.env_texture = xStrHash("rainbowfilm_smooth32"); - } -} + return current_tweak->missle.explode.hit_duration - state->hit_time; + } -void cruise_bubble::init() -{ - if ((shared.flags & 0x1) != 0x1) - { - return; - } + void cruise_bubble::get_explode_sphere(xVec3& center, F32& radius) + { + state_missle_explode* state = (state_missle_explode*)shared.state[THREAD_MISSLE]; + if (state == NULL || state->type != STATE_MISSLE_EXPLODE) + { + return; + } + if (state->hit_time >= current_tweak->missle.explode.hit_duration) + { + return; + } - cruise_bubble::init_sound(); + center = shared.hit_loc; + radius = state->get_radius(); + } - shared.flags = shared.flags | 0x2; + F32 cruise_bubble::state_missle_explode::get_radius() const + { + F32 t_frac = this->hit_time / current_tweak->missle.explode.hit_duration; + return t_frac * current_tweak->missle.explode.hit_radius; + } - cruise_bubble::load_settings(); - cruise_bubble::init_states(); - cruise_bubble::init_missle_model(); - cruise_bubble::init_wake_ribbons(); - cruise_bubble::init_explode_decal(); - cruise_bubble::init_shrapnel(); - cruise_bubble::init_hud(); - cruise_bubble::init_debug(); + xEnt** cruise_bubble::get_explode_hits(S32& size) + { + state_missle_explode* state = (state_missle_explode*)shared.state[THREAD_MISSLE]; + if (state == NULL || state->type != STATE_MISSLE_EXPLODE) + { + size = 0; + return NULL; + } + if (state->hit_time >= current_tweak->missle.explode.hit_duration) + { + size = 0; + return NULL; + } - // scheduling off - shared.fov_default = xCameraGetFOV(&globals.camera); - shared.dialog_freq = current_tweak->dialog.freq; -} + size = shared.hits_size; + return shared.hits; + } -void cruise_bubble::reset() -{ - if ((shared.flags & 0x3) != 0x3) - { - return; - } - cruise_bubble::kill(true, false); -} + // param names guessed + void cruise_bubble::add_life(F32 life, F32 max) + { + state_missle_fly* state = (state_missle_fly*)shared.state[THREAD_MISSLE]; + if (state == NULL || state->type != STATE_MISSLE_FLY) + { + return; + } -void cruise_bubble::launch() -{ - if ((shared.flags & 0x13) != 0x3) - { - return; - } + state->life += life; - if ((zGameExtras_CheatFlags() & 0x20000000) != 0) - { - // scheduling off - shared.flags = shared.flags | 0x200; - current_tweak = &cheat_tweak; - } - else - { - current_tweak = &normal_tweak; - } + if (max < 0.0f) + { + max = current_tweak->missle.life; + } - cruise_bubble::reset_wake_ribbons(); - cruise_bubble::reset_explode_decal(); + if (!(max > 0.0f)) + { + return; + } + if (!(state->life > max)) + { + return; + } - shared.flags = shared.flags | 0x14; - shared.last_sp = shared.sp = globals.pad0->analog[0].offset; - shared.player_health = globals.player.Health; - // scheduling off - shared.player_motion = zEntCruiseBubble_f_0_0; - shared.fov_default = xCameraGetFOV(&globals.camera); + state->life = max; + } - ztalkbox::permit(0x0, 0xffffffff); - cruise_bubble::set_state(THREAD_PLAYER, BEGIN_STATE_PLAYER); -} + void cruise_bubble::set_life(F32 life) + { + state_missle_fly* state = (state_missle_fly*)shared.state[THREAD_MISSLE]; + if (state == NULL || state->type != STATE_MISSLE_FLY) + { + return; + } -bool cruise_bubble::update(xScene* s, F32 dt) -{ - if ((shared.flags & 0x3) != 0x3) - { - return false; - } + state->life = life; + } - if ((shared.flags & 0x10) == 0x0) - { - if (cruise_bubble::check_launch()) + void cruise_bubble::reset_life() { - launch(); + state_missle_fly* state = (state_missle_fly*)shared.state[THREAD_MISSLE]; + if (state == NULL || state->type != STATE_MISSLE_FLY) + { + return; + } + + state->life = current_tweak->missle.life; } - else + + bool cruise_bubble::event_handler(xBase* from, U32 event, const F32* fparam, xBase* to) { + switch (event) + { + case eEventCruiseAddLife: + add_life(fparam[0], fparam[1]); + return true; + + case eEventCruiseSetLife: + set_life(fparam[0]); + return true; + + case eEventCruiseResetLife: + reset_life(); + return true; + + case eEventCruiseFired: + case eEventCruiseDied: + return true; + } + return false; } - } - - if (globals.player.ControlOff) - { - cruise_bubble::kill(true, false); - return false; - } - cruise_bubble::refresh_controls(); - cruise_bubble::update_state(s, dt); + void cruise_bubble::state_player_halt::start() + { + this->first_update = true; + } - if ((shared.flags & 0x10) == 0) - { - return false; - } + void cruise_bubble::state_player_halt::stop() + { + shared.flags &= 0xffffffdf; + } - cruise_bubble::update_player(*s, dt); - cruise_bubble::update_missle(*s, dt); - cruise_bubble::update_hud(dt); - return true; -} + cruise_bubble::state_enum cruise_bubble::state_player_halt::update(F32 dt) + { + this->time += dt; -bool cruise_bubble::render() -{ - if ((shared.flags & 0x7) != 0x7) - { - return false; - } + if (this->first_update) + { + this->first_update = false; + this->last_motion = shared.player_motion; + return STATE_PLAYER_HALT; + } - cruise_bubble::render_state(); - cruise_bubble::render_player(); - cruise_bubble::render_missle(); - cruise_bubble::render_debug(); + xVec3 dmotion = shared.player_motion - this->last_motion; + if (dmotion.length2() < 0.0001f) + { + return STATE_PLAYER_AIM; + } - return true; -} + if (this->time > current_tweak->player.halt_time) + { + return STATE_INVALID; + } -void cruise_bubble::render_screen() -{ - if ((shared.flags & 0x7) != 0x7) - { - return; - } - cruise_bubble::render_hud(); -} + return STATE_PLAYER_HALT; + } -void cruise_bubble::insert_player_animations(xAnimTable& table) -{ - // not matching for different reasons - // register scheduling - // register allocation, caching stringBase at reused offsets - // closest to a match i got was by incrementing the offsets from stringBase such that they're all unique - // after that its pretty clear that this is a functional match + void cruise_bubble::state_player_aim::start() + { + shared.flags |= 0x20; - if (shared.astate.player.aim != NULL) - { - return; - } + xVec3* player_dir = &get_player_mat()->at; + this->yaw = xatan2(player_dir->x, player_dir->z); + this->yaw_vel = 0.0f; + this->turn_delay = 0.0f; - shared.astate.player.aim = - xAnimTableNewState(&table, "cruise_bubble_aim", 0x10, 0, zEntCruiseBubble_f_1_0, NULL, NULL, - zEntCruiseBubble_f_0_0, NULL, NULL, xAnimDefaultBeforeEnter, NULL, NULL); - - shared.astate.player.fire = - xAnimTableNewState(&table, "cruise_bubble_fire", 0x20, 0, zEntCruiseBubble_f_1_0, NULL, - NULL, zEntCruiseBubble_f_0_0, NULL, NULL, xAnimDefaultBeforeEnter, NULL, - NULL); - - shared.astate.player.idle = - xAnimTableNewState(&table, "cruise_bubble_idle", 0x10, 0, zEntCruiseBubble_f_1_0, NULL, - NULL, zEntCruiseBubble_f_0_0, NULL, NULL, xAnimDefaultBeforeEnter, NULL, - NULL); - - char* start_from = (char*)xMemPushTemp(0x250); - memset(start_from, 0, 0x250); - char* s = start_from; - *s = '\0'; - for (U32 i = 0; i < 37; ++i) - { - strcat(s, start_anim_states[i]); - s += strlen(s); - *s = ' '; - *++s = '\0'; - } + xAnimPlayStartTransition(globals.player.ent.model->Anim, shared.atran.player.aim); + cruise_bubble::set_state(THREAD_CAMERA, STATE_CAMERA_AIM); + } - shared.atran.player.aim = - xAnimTableNewTransition(&table, start_from, "cruise_bubble_aim", - (xAnimTransitionConditionalCallback)&check_anim_aim, NULL, 0, 0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_0_0, 0, 0, - zEntCruiseBubble_f_0_15, NULL); + xMat4x3* get_player_mat() + { + return (xMat4x3*)globals.player.ent.model->Mat; + } - shared.atran.player.fire = - xAnimTableNewTransition(&table, "cruise_bubble_aim", "cruise_bubble_fire", NULL, NULL, 0, 0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_0_0, 0, 0, - zEntCruiseBubble_f_0_15, NULL); + void cruise_bubble::state_player_aim::stop() + { + shared.flags &= 0xffffffdf; + } - shared.atran.player.idle = - xAnimTableNewTransition(&table, "cruise_bubble_fire", "cruise_bubble_idle", NULL, NULL, - 0x10, 0, zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_0_0, 0, 0, - zEntCruiseBubble_f_0_15, NULL); + cruise_bubble::state_enum cruise_bubble::state_player_aim::update(F32 dt) + { + this->turn_delay += dt; - shared.atran.player.end = - xAnimTableNewTransition(&table, "cruise_bubble_aim cruise_bubble_fire cruise_bubble_idle", - "Idle01", NULL, NULL, 0, 0, zEntCruiseBubble_f_0_0, - zEntCruiseBubble_f_0_0, 0, 0, zEntCruiseBubble_f_0_15, NULL); + if (this->turn_delay >= current_tweak->aim_delay) + { + this->face_camera(dt); + } + this->apply_yaw(); + this->update_animation(dt); - xMemPopTemp(start_from); -} + if (!(globals.pad0->on & 0x100)) + { + return STATE_PLAYER_FIRE; + } + return STATE_PLAYER_AIM; + } -xAnimTable* cruise_bubble::anim_table() -{ - xAnimTable* table = xAnimTableNew("Cruise Bubble", 0, 0); - shared.astate.missle.fire = xAnimTableNewState(table, "fire", 0x20, 0, 1.0f, NULL, NULL, 0.0f, NULL, NULL, xAnimDefaultBeforeEnter, NULL, NULL); - shared.astate.missle.fly = xAnimTableNewState(table, "fly", 0x10, 0, 1.0f, NULL, NULL, 0.0f, NULL, NULL, xAnimDefaultBeforeEnter, NULL, NULL); - shared.atran.missle.fly = xAnimTableNewTransition(table, "fire", "fly", NULL, NULL, 0x10, 0, 0.0f, 0.0f, 0, 0, 0.15f, NULL); - return table; -} + void cruise_bubble::state_player_aim::update_animation(F32 dt) + { + F32 r = range_limit(this->yaw_vel * current_tweak->player.aim.anim_delta, + -1.0f, 1.0f); + xAnimSingle* s = globals.player.ent.model->Anim->Single; + s->BilinearLerp[0] = 0.5f * ((1.0f + s->BilinearLerp[0]) + r); + } -bool cruise_bubble::active() -{ - return shared.flags & 0x10; -} + void cruise_bubble::state_player_aim::apply_yaw() + { + xMat4x3* m = cruise_bubble::get_player_mat(); + m->at.assign(isin(this->yaw), 0.0f, icos(this->yaw)); + m->right.assign(m->at.z, 0.0f, -m->at.x); + m->up.assign(0.0f, 1.0f, 0.0f); + } -F32 cruise_bubble::exploding() -{ - state_missle_explode* state = (state_missle_explode*)shared.state[THREAD_MISSLE]; - if (state == NULL || state->type != STATE_MISSLE_EXPLODE) - { - return zEntCruiseBubble_f_0_0; - } + void cruise_bubble::state_player_aim::face_camera(F32 dt) + { + xMat4x3* mat = &globals.camera.mat; - return current_tweak->missle.explode.hit_duration - state->hit_time; -} + F32 new_yaw; + if (mat->at.x >= -0.00001f && mat->at.x <= 0.00001f && + mat->at.z >= -0.00001f && mat->at.z <= 0.00001f) + { + new_yaw = this->yaw; + } + else + { + new_yaw = xatan2(mat->at.x, mat->at.z); + } -void cruise_bubble::get_explode_sphere(xVec3& center, F32& radius) -{ - state_missle_explode* state = (state_missle_explode*)shared.state[THREAD_MISSLE]; - if (state == NULL || state->type != STATE_MISSLE_EXPLODE) - { - return; - } - if (state->hit_time >= current_tweak->missle.explode.hit_duration) - { - return; - } + F32 diff = new_yaw - this->yaw; + if (diff > PI) + { + diff -= PI * 2; + } + else if (diff < -PI) + { + diff += PI * 2; + } + F32 tspeed = current_tweak->player.aim.turn_speed * xexp(dt); + if (tspeed > 1.0f) + { + tspeed = 1.0f; + } + tspeed = diff * tspeed; + this->yaw_vel = tspeed / dt; + this->yaw = this->yaw + tspeed; + this->yaw = xrmod(this->yaw); + } - center = shared.hit_loc; - radius = state->get_radius(); -} + void cruise_bubble::state_player_fire::start() + { + this->wand_shown = false; -F32 cruise_bubble::state_missle_explode::get_radius() const -{ - F32 t_frac = this->hit_time / current_tweak->missle.explode.hit_duration; - return t_frac * current_tweak->missle.explode.hit_radius; -} + cruise_bubble::play_sound(0, 1.0f, &get_missle_mat()->pos); + xAnimPlayStartTransition(globals.player.ent.model->Anim, shared.atran.player.fire); + cruise_bubble::set_state(THREAD_MISSLE, STATE_MISSLE_APPEAR); -xEnt** cruise_bubble::get_explode_hits(S32& size) -{ - state_missle_explode* state = (state_missle_explode*)shared.state[THREAD_MISSLE]; - if (state == NULL || state->type != STATE_MISSLE_EXPLODE) - { - size = 0; - return NULL; - } - if (state->hit_time >= current_tweak->missle.explode.hit_duration) - { - size = 0; - return NULL; - } + if (xurand() <= shared.dialog_freq) + { + play_sound(3, 1.0f); + shared.dialog_freq *= current_tweak->dialog.decay; - size = shared.hits_size; - return shared.hits; -} + if (shared.dialog_freq < current_tweak->dialog.min_freq) + { + shared.dialog_freq = current_tweak->dialog.min_freq; + } + } + } -// param names guessed -void cruise_bubble::add_life(F32 life, F32 max) -{ - state_missle_fly* state = (state_missle_fly*)shared.state[THREAD_MISSLE]; - if (state == NULL || state->type != STATE_MISSLE_FLY) - { - return; - } + xMat4x3* get_missle_mat() + { + return (xMat4x3*)shared.missle_model->Mat; + } - state->life += life; + void cruise_bubble::state_player_fire::stop() + { + cruise_bubble::hide_wand(); + } - if (max < 0.0f) - { - max = current_tweak->missle.life; - } + cruise_bubble::state_enum cruise_bubble::state_player_fire::update(F32 dt) + { + xAnimSingle* asingle = globals.player.ent.model->Anim->Single; + xAnimState* astate = asingle->State; - if (!(max > 0.0f)) - { - return; - } - if (!(state->life > max)) - { - return; - } + if (astate == shared.astate.player.fire) + { + F32 time = astate->Data->Duration; + F32 max_time = asingle->Time + dt; - state->life = max; -} + if (this->wand_shown == 0 && max_time >= current_tweak->player.fire.delay_wand) + { + show_wand(); + } + if (max_time >= time) + { + return STATE_PLAYER_WAIT; + } + } -void cruise_bubble::set_life(F32 life) -{ - state_missle_fly* state = (state_missle_fly*)shared.state[THREAD_MISSLE]; - if (state == NULL || state->type != STATE_MISSLE_FLY) - { - return; - } + if (this->wand_shown != 0) + { + this->update_wand(dt); + } - state->life = life; -} + return STATE_PLAYER_FIRE; + } -void cruise_bubble::reset_life() -{ - state_missle_fly* state = (state_missle_fly*)shared.state[THREAD_MISSLE]; - if (state == NULL || state->type != STATE_MISSLE_FLY) - { - return; - } + void cruise_bubble::state_player_fire::update_wand(F32 dt) + { + // empty + } - state->life = current_tweak->missle.life; -} + void cruise_bubble::state_player_wait::start() + { + cruise_bubble::hide_wand(); + } -bool cruise_bubble::event_handler(xBase* from, U32 event, const F32* fparam, xBase* to) -{ - switch (event) - { - case 0x205: - add_life(fparam[0], fparam[1]); - return true; + cruise_bubble::state_enum cruise_bubble::state_player_wait::update(F32) + { + return STATE_PLAYER_WAIT; + } - case 0x206: - set_life(fparam[0]); - return true; + void cruise_bubble::state_missle_appear::start() + { + cruise_bubble::show_missle(); + shared.missle_model->Flags = shared.missle_model->Flags & 0xfffe; + shared.missle_model->Alpha = 1.0f; + xAnimPlaySetState(shared.missle_model->Anim->Single, shared.astate.missle.fire, + 0.0f); + this->move(); + } - case 0x207: - reset_life(); - return true; + void cruise_bubble::state_missle_appear::move() + { + xMat4x3& mat = *cruise_bubble::get_missle_mat(); + xVec3 euler; + xVec3 prod; - case 0x203: - case 0x204: - return true; - } + mat = *cruise_bubble::get_player_mat(); + xMat3x3GetEuler(&mat, &euler); + xMat3x3Euler(&mat, &euler); + xMat3x3RMulVec(&prod, &mat, ¤t_tweak->missle.appear.offset); + mat.pos += prod; + } - return false; -} + void cruise_bubble::state_missle_appear::stop() + { + hide_missle(); + } -void cruise_bubble::state_player_halt::start() -{ - this->first_update = true; -} + cruise_bubble::state_enum cruise_bubble::state_missle_appear::update(F32 dt) + { + F32 time = shared.missle_model->Anim->Single->Time + dt; + if (time >= current_tweak->missle.appear.delay_show) + { + shared.missle_model->Flags = shared.missle_model->Flags | 0x1; + start_trail(); + } + move(); -void cruise_bubble::state_player_halt::stop() -{ - shared.flags &= 0xffffffdf; -} + if (time >= current_tweak->missle.appear.delay_fly) + { + return STATE_MISSLE_FLY; + } -cruise_bubble::state_enum cruise_bubble::state_player_halt::update(F32 dt) -{ - this->time += dt; + this->update_effects(dt); + return STATE_MISSLE_APPEAR; + } - if (this->first_update) - { - this->first_update = false; - this->last_motion = shared.player_motion; - return STATE_PLAYER_HALT; - } + void cruise_bubble::state_missle_appear::update_effects(F32 dt) + { + //empty + } - xVec3 dmotion = shared.player_motion - this->last_motion; - if (dmotion.length2() < zEntCruiseBubble_f_0_0001) - { - return STATE_PLAYER_AIM; - } + void cruise_bubble::state_missle_fly::start() + { + // lwzu + // & to match function size + shared.flags |= 0x8; + this->life = current_tweak->missle.life; - if (this->time > current_tweak->player.halt_time) - { - return STATE_INVALID; - } + cruise_bubble::show_missle(); + start_trail(); + cruise_bubble::start_damaging(); - return STATE_PLAYER_HALT; -} + this->vel = 0.0f; + xMat3x3GetEuler(cruise_bubble::get_missle_mat(), &this->rot); + this->rot_vel = 0.0f; + this->engine_pitch = 0.0f; + this->flash_time = 0.0f; + this->last_loc = globals.player.ent.bound.sph.center; -void cruise_bubble::state_player_aim::start() -{ - shared.flags |= 0x20; + missle_record.reset(); + missle_record.push_front(missle_record_data(this->last_loc, this->rot.z)); + missle_record.push_front(missle_record_data(cruise_bubble::get_missle_mat()->pos, this->rot.z)); - xVec3* player_dir = &get_player_mat()->at; - this->yaw = xatan2(player_dir->x, player_dir->z); - this->yaw_vel = zEntCruiseBubble_f_0_0; - this->turn_delay = zEntCruiseBubble_f_0_0; + play_sound(2, 1.0f, &cruise_bubble::get_missle_mat()->pos); + signal_event(eEventCruiseFired); + } - xAnimPlayStartTransition(globals.player.ent.model->Anim, shared.atran.player.aim); - cruise_bubble::set_state(THREAD_CAMERA, STATE_CAMERA_AIM); -} + missle_record_data::missle_record_data(const xVec3& loc, F32 roll) : loc(loc), roll(roll) + { + } -void cruise_bubble::state_player_aim::stop() -{ - shared.flags &= 0xffffffdf; -} + void cruise_bubble::state_missle_fly::stop() + { + shared.flags &= 0xfffffff7; -cruise_bubble::state_enum cruise_bubble::state_player_aim::update(F32 dt) -{ - this->turn_delay += dt; + hide_missle(); + stop_trail(); + stop_sound(2, 0); + signal_event(eEventCruiseDied); - if (this->turn_delay >= current_tweak->aim_delay) - { - this->face_camera(dt); - } - this->apply_yaw(); - this->update_animation(dt); + xSphere o; + o.center = 1e38; + o.r = 0.0f; + notify_triggers(*globals.sceneCur, o, xVec3::create(0.0f)); + } - if ((globals.pad0->on & 0x100) == 0) - { - return STATE_PLAYER_FIRE; - } - return STATE_PLAYER_AIM; -} + void cruise_bubble::state_missle_fly::abort() + { + stop_sound(2, 0); + signal_event(eEventCruiseDied); -void cruise_bubble::state_player_aim::update_animation(F32 dt) -{ - F32 r = range_limit(this->yaw_vel * current_tweak->player.aim.anim_delta, - zEntCruiseBubble_f_n1_0, zEntCruiseBubble_f_1_0); - xAnimSingle* s = globals.player.ent.model->Anim->Single; - s->BilinearLerp[0] = zEntCruiseBubble_f_0_5 * ((1.0f + s->BilinearLerp[0]) + r); -} + xSphere o; + o.center = 1e38; + o.r = 0.0f; + notify_triggers(*globals.sceneCur, o, xVec3::create(0.0f)); + } -void cruise_bubble::state_player_aim::apply_yaw() -{ - xMat4x3* m = cruise_bubble::get_player_mat(); - m->at.assign(isin(this->yaw), zEntCruiseBubble_f_0_0, icos(this->yaw)); - m->right.assign(m->at.z, zEntCruiseBubble_f_0_0, -m->at.x); - m->up.assign(zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_1_0, zEntCruiseBubble_f_0_0); -} + cruise_bubble::state_enum cruise_bubble::state_missle_fly::update(F32 dt) + { + this->life -= dt; + if (this->life <= 0.0f || (globals.pad0->pressed & 0x100)) + { + shared.hit_loc = get_missle_mat()->pos; + shared.hit_norm = get_missle_mat()->at; -void cruise_bubble::state_player_aim::face_camera(F32 dt) -{ - // Externed constants needed for match for some reason. - xMat4x3* mat = &globals.camera.mat; + return STATE_MISSLE_EXPLODE; + } - F32 new_yaw; - if (mat->at.x >= zEntCruiseBubble_f_n0_00001 && mat->at.x <= 0.00001f && - mat->at.z >= zEntCruiseBubble_f_n0_00001 && mat->at.z <= 0.00001f) - { - new_yaw = this->yaw; - } - else - { - new_yaw = xatan2(mat->at.x, mat->at.z); - } + this->update_turn(dt); + this->update_move(dt); - F32 diff = new_yaw - this->yaw; - if (diff > PI) - { - diff -= PI * 2; - } - else if (diff < zEntCruiseBubble_f_n3_1415) - { - diff += PI * 2; - } - F32 tspeed = current_tweak->player.aim.turn_speed * xexp(dt); - if (tspeed > 1.0f) - { - tspeed = 1.0f; - } - tspeed = diff * tspeed; - this->yaw_vel = tspeed / dt; - this->yaw = this->yaw + tspeed; - this->yaw = xrmod(this->yaw); -} + if (missle_record.full()) + { + missle_record.pop_back(); + } -void cruise_bubble::state_player_fire::start() -{ - this->wand_shown = false; + if (this->collide() || this->collide_hazards()) + { + missle_record.push_front(missle_record_data(shared.hit_loc, this->rot.z)); + return STATE_MISSLE_EXPLODE; + } + missle_record.push_front(missle_record_data(get_missle_mat()->pos, this->rot.z)); - cruise_bubble::play_sound(0, zEntCruiseBubble_f_1_0, &get_missle_mat()->pos); - xAnimPlayStartTransition(globals.player.ent.model->Anim, shared.atran.player.fire); - cruise_bubble::set_state(THREAD_MISSLE, STATE_MISSLE_APPEAR); + this->last_loc = get_missle_mat()->pos; + this->update_engine_sound(dt); + this->update_flash(dt); - if (xurand() <= shared.dialog_freq) - { - play_sound(3, zEntCruiseBubble_f_1_0); - shared.dialog_freq *= current_tweak->dialog.decay; + xMat4x3* mat = get_missle_mat(); + xSphere o; + o.center = mat->pos; + o.r = current_tweak->missle.hit_dist; + notify_triggers(*globals.sceneCur, o, mat->at); - if (shared.dialog_freq < current_tweak->dialog.min_freq) - { - shared.dialog_freq = current_tweak->dialog.min_freq; + return STATE_MISSLE_FLY; } - } -} -void cruise_bubble::state_player_fire::stop() -{ - cruise_bubble::hide_wand(); -} - -cruise_bubble::state_enum cruise_bubble::state_player_fire::update(F32 dt) -{ - xAnimSingle* asingle = globals.player.ent.model->Anim->Single; - xAnimState* astate = asingle->State; + void cruise_bubble::state_missle_fly::update_flash(F32 dt) + { + this->flash_time += dt; - if (astate == shared.astate.player.fire) - { - F32 time = astate->Data->Duration; - F32 max_time = asingle->Time + dt; + F32 tflash = + current_tweak->missle.fly.flash_interval * (this->life / current_tweak->missle.life); + if (this->flash_time > tflash) + { + flash_hud(); - if (this->wand_shown == 0 && max_time >= current_tweak->player.fire.delay_wand) - { - show_wand(); + F32 floor_frac = std::floorf(this->flash_time / tflash); + this->flash_time = this->flash_time - (floor_frac * tflash); + } } - if (max_time >= time) + + void cruise_bubble::state_missle_fly::update_engine_sound(F32 dt) { - return STATE_PLAYER_WAIT; - } - } + F32 tmp = current_tweak->missle.fly.engine_pitch_max * + (iabs(shared.last_sp.x) + iabs(shared.last_sp.y)); - if (this->wand_shown != 0) - { - this->update_wand(dt); - } + this->engine_pitch += + (tmp - this->engine_pitch) * current_tweak->missle.fly.engine_pitch_sensitivity; - return STATE_PLAYER_FIRE; -} + set_pitch(2, this->engine_pitch, 0); + } -void cruise_bubble::state_player_fire::update_wand(F32 dt) -{ - // empty -} + U8 cruise_bubble::state_missle_fly::collide_hazards() + { + NPCHazard* c[2]; + c[0] = NULL; + HAZ_Iterate(&cruise_bubble::state_missle_fly::hazard_check, c, 0xa000); -void cruise_bubble::state_player_wait::start() -{ - cruise_bubble::hide_wand(); -} + if (c[0] == NULL) + { + return false; + } -cruise_bubble::state_enum cruise_bubble::state_player_wait::update(F32) -{ - return STATE_PLAYER_WAIT; -} + if ((c[0]->flg_hazard & 0x200000)) + { + c[0]->MarkForRecycle(); + } + shared.hit_loc = cruise_bubble::get_missle_mat()->pos; + return true; + } -void cruise_bubble::state_missle_appear::start() -{ - cruise_bubble::show_missle(); - shared.missle_model->Flags = shared.missle_model->Flags & 0xfffe; - shared.missle_model->Alpha = zEntCruiseBubble_f_1_0; - xAnimPlaySetState(shared.missle_model->Anim->Single, shared.astate.missle.fire, - zEntCruiseBubble_f_0_0); - this->move(); -} + bool cruise_bubble::state_missle_fly::hazard_check(NPCHazard& haz, void* context) + { + // get_missle_mat()->pos uses one more instruction for no apparent reason + xVec3 vvar = haz.pos_hazard - get_missle_mat()->pos; + F32 fvar = current_tweak->missle.hit_dist + haz.custdata.typical.rad_cur; -void cruise_bubble::state_missle_appear::move() -{ - xMat4x3& mat = *cruise_bubble::get_missle_mat(); - xVec3 euler; - xVec3 prod; - - mat = *cruise_bubble::get_player_mat(); - xMat3x3GetEuler(&mat, &euler); - xMat3x3Euler(&mat, &euler); - xMat3x3RMulVec(&prod, &mat, ¤t_tweak->missle.appear.offset); - mat.pos += prod; -} + // scheduling for implicit copy ctor off + if (vvar.length2() < fvar * fvar) + { + ((NPCHazard**)context)[0] = &haz; + return false; + } -void cruise_bubble::state_missle_appear::stop() -{ - hide_missle(); -} + return true; + } -cruise_bubble::state_enum cruise_bubble::state_missle_appear::update(F32 dt) -{ - F32 time = shared.missle_model->Anim->Single->Time + dt; - if (time >= current_tweak->missle.appear.delay_show) - { - shared.missle_model->Flags = shared.missle_model->Flags | 0x1; - start_trail(); - } - move(); + U8 cruise_bubble::state_missle_fly::collide() + { + xVec3* hit_norm = &shared.hit_norm; + xVec3* hit_loc = &shared.hit_loc; + xEnt* hit_ent; + xVec3 hit_depen; + S32 i = 0; - if (time >= current_tweak->missle.appear.delay_fly) - { - return STATE_MISSLE_FLY; - } + do + { + if (!this->hit_test(*hit_loc, *hit_norm, hit_depen, hit_ent)) + { + return false; + } - this->update_effects(dt); - return STATE_MISSLE_APPEAR; -} + if (cruise_bubble::can_damage(hit_ent)) + { + damage_entity(*hit_ent, *hit_loc, cruise_bubble::get_missle_mat()->at, *hit_norm, + 0.0f, false); + return true; + } -void cruise_bubble::state_missle_appear::update_effects(F32 dt) -{ - //empty -} + xMat4x3* mat = cruise_bubble::get_missle_mat(); + F32 ang = xasin(mat->at.dot(*hit_norm)); + if (ang < -current_tweak->missle.crash_angle) + { + return true; + } -void cruise_bubble::state_missle_fly::start() -{ - // lwzu - // & to match function size - shared.flags |= 0x8; - this->life = current_tweak->missle.life; - - cruise_bubble::show_missle(); - start_trail(); - cruise_bubble::start_damaging(); - - this->vel = zEntCruiseBubble_f_0_0; - xMat3x3GetEuler(cruise_bubble::get_missle_mat(), &this->rot); - this->rot_vel = zEntCruiseBubble_f_0_0; - this->engine_pitch = zEntCruiseBubble_f_0_0; - this->flash_time = zEntCruiseBubble_f_0_0; - this->last_loc = globals.player.ent.bound.sph.center; - - missle_record.reset(); - missle_record.push_front(missle_record_data(this->last_loc, this->rot.z)); - missle_record.push_front(missle_record_data(cruise_bubble::get_missle_mat()->pos, this->rot.z)); - - play_sound(2, zEntCruiseBubble_f_1_0, &cruise_bubble::get_missle_mat()->pos); - signal_event(0x203); -} + mat->pos += hit_depen; + xVec3 diff = mat->pos - this->last_loc; + F32 len = diff.length2(); + if (len < 0.001f) + { + return false; + } -cruise_bubble::missle_record_data::missle_record_data(const xVec3& loc, F32 roll) - : loc(loc), roll(roll) -{ -} + len = xsqrt(len); + xVec3 diff_normalized = diff * (1.0f / len); + F32 sin = -xasin(diff_normalized.y); + if (F32(iabs(sin)) > (PI / 2) * current_tweak->missle.fly.turn.ybound) + { + return true; + } -void cruise_bubble::state_missle_fly::stop() -{ - shared.flags &= 0xfffffff7; + F32 tan = xatan2(diff_normalized.x, diff_normalized.z); + F32 mod = + xrmod(PI + (tan - this->rot.x)) - PI; + this->rot.x += mod * current_tweak->missle.collide_twist; + this->rot.y += (sin - this->rot.y) * current_tweak->missle.collide_twist; + xMat3x3Euler(mat, &this->rot); - hide_missle(); - stop_trail(); - stop_sound(2, 0); - signal_event(0x204); + if (hit_depen.length2() < 1.0000001e-6f) + { + return false; + } + } while (++i < current_tweak->missle.hit_tests); - xSphere o; - o.center = zEntCruiseBubble_f_1_0e38; - o.r = zEntCruiseBubble_f_0_0; - notify_triggers(*globals.sceneCur, o, xVec3::create(zEntCruiseBubble_f_0_0)); -} + F32 dist = 0.1f * current_tweak->missle.hit_dist; + if (hit_depen.length2() < dist * dist) + { + return false; + } -void cruise_bubble::state_missle_fly::abort() -{ - stop_sound(2, 0); - signal_event(0x204); + return this->hit_test(*hit_loc, *hit_norm, hit_depen, hit_ent); + } - xSphere o; - o.center = zEntCruiseBubble_f_1_0e38; - o.r = zEntCruiseBubble_f_0_0; - notify_triggers(*globals.sceneCur, o, xVec3::create(zEntCruiseBubble_f_0_0)); -} + U8 cruise_bubble::state_missle_fly::hit_test(xVec3& hit_loc, xVec3& hit_norm, xVec3& hit_depen, + xEnt*& hit_ent) const + { + xScene* s = globals.sceneCur; + xVec3* loc = &get_missle_mat()->pos; + xSweptSphere ss; + xSweptSpherePrepare(&ss, (xVec3*)&this->last_loc, loc, current_tweak->missle.hit_dist); + ss.optr = NULL; + if (!xSweptSphereToScene(&ss, s, NULL, 0x10)) + { + return false; + } -cruise_bubble::state_enum cruise_bubble::state_missle_fly::update(F32 dt) -{ - this->life -= dt; - if (this->life <= zEntCruiseBubble_f_0_0 || (globals.pad0->pressed & 0x100) != 0) - { - shared.hit_loc = get_missle_mat()->pos; - shared.hit_norm = get_missle_mat()->at; + xSweptSphereGetResults(&ss); + // scheduling off + xVec3 overshoot = {}; + overshoot.x = loc->x - ss.worldPos.x; + overshoot.y = loc->y - ss.worldPos.y; + overshoot.z = loc->z - ss.worldPos.z; + // till here + hit_loc = ss.worldPos + ss.worldTangent * overshoot.dot(ss.worldTangent); + hit_depen = hit_loc - *loc; + hit_norm = ss.worldNormal; + hit_ent = (xEnt*)ss.optr; - return STATE_MISSLE_EXPLODE; - } + return true; + } - this->update_turn(dt); - this->update_move(dt); + void cruise_bubble::state_missle_fly::update_move(F32 dt) + { + F32 accel = current_tweak->missle.fly.accel; + F32 max = current_tweak->missle.fly.max_vel; + F32 move = 0.0f; - if (missle_record.full()) - { - missle_record.pop_back(); - } + xAccelMove(move, this->vel, accel, dt, max); - if (this->collide() || this->collide_hazards()) - { - missle_record.push_front(missle_record_data(shared.hit_loc, this->rot.z)); - return STATE_MISSLE_EXPLODE; - } - missle_record.push_front(missle_record_data(get_missle_mat()->pos, this->rot.z)); + xMat4x3* mat = get_missle_mat(); + mat->pos += mat->at * move; + } - this->last_loc = get_missle_mat()->pos; - this->update_engine_sound(dt); - this->update_flash(dt); + void cruise_bubble::state_missle_explode::start() + { + shared.flags |= 0x40; + // scheduling for zEntCruiseBubble_f_0_0 + this->hit_time = 0.0f; - xMat4x3* mat = get_missle_mat(); - xSphere o; - o.center = mat->pos; - o.r = current_tweak->missle.hit_dist; - notify_triggers(*globals.sceneCur, o, mat->at); + if (current_tweak->missle.explode.hit_duration <= 0.0f) + this->apply_damage(current_tweak->missle.explode.hit_radius); - return STATE_MISSLE_FLY; -} + F32 dist = (shared.hit_loc - get_player_loc()).length2(); + F32 mul = current_tweak->camera.survey.min_dist * current_tweak->camera.survey.min_dist; + F32 min_dist = mul; + set_state(THREAD_CAMERA, dist <= min_dist ? STATE_CAMERA_RESTORE : STATE_CAMERA_SURVEY); -void cruise_bubble::state_missle_fly::update_flash(F32 dt) -{ - this->flash_time += dt; + play_sound(1, 1.0f, &get_missle_mat()->pos); + this->start_effects(); + } - F32 tflash = - current_tweak->missle.fly.flash_interval * (this->life / current_tweak->missle.life); - if (this->flash_time > tflash) - { - flash_hud(); + void cruise_bubble::state_missle_explode::start_effects() + { + U32 rand; + U32 emit_max; + U32 emit_min; + zShrapnelAsset* shrap; - F32 floor_frac = std::floorf(this->flash_time / tflash); - this->flash_time = this->flash_time - (floor_frac * tflash); - } -} + // current_tweak loaded into r30 instead of r29. + zFX_SpawnBubbleBlast(&get_missle_mat()->pos, current_tweak->blast.emit, current_tweak->blast.radius, current_tweak->blast.vel, current_tweak->blast.rand_vel); -void cruise_bubble::state_missle_fly::update_engine_sound(F32 dt) -{ - F32 tmp = current_tweak->missle.fly.engine_pitch_max * - (iabs(shared.last_sp.x) + iabs(shared.last_sp.y)); + xVec3 scale = { 1.0f, 1.0f, 1.0f }; - this->engine_pitch += - (tmp - this->engine_pitch) * current_tweak->missle.fly.engine_pitch_sensitivity; + explode_decal.emit(*get_missle_mat(), scale, -1); - set_pitch(2, this->engine_pitch, 0); -} + shrap = shared.droplet_shrapnel; + if ((shrap != NULL) && (shrap->initCB != NULL)) + { + emit_max = current_tweak->droplet.emit_min; + emit_min = current_tweak->droplet.emit_max; -U8 cruise_bubble::state_missle_fly::collide_hazards() -{ - NPCHazard* c[2]; - c[0] = NULL; - HAZ_Iterate(&cruise_bubble::state_missle_fly::hazard_check, c, 0xa000); + if (emit_max >= emit_min) + { + emit_max = emit_min; + } + else + { + rand = xrand(); + emit_max += (rand / 0x2000) - ((rand / 0x2000) / (emit_min - emit_max)) * (emit_min - emit_max); + } - if (c[0] == NULL) - { - return false; - } + ((state_missle_explode*)(emit_max))->reset_quadrants((S32)current_tweak, current_tweak->droplet.vel_angle); - if ((c[0]->flg_hazard & 0x200000) != 0) - { - c[0]->MarkForRecycle(); - } - shared.hit_loc = cruise_bubble::get_missle_mat()->pos; - return true; -} + for (U32 i = 0; i < emit_max; i++) + { + // shrap->initCB(shrap, shared.missle_model, NULL, cb_droplet); + } + } + } -bool cruise_bubble::state_missle_fly::hazard_check(NPCHazard& haz, void* context) -{ - // get_missle_mat()->pos uses one more instruction for no apparent reason - xVec3 vvar = haz.pos_hazard - get_missle_mat()->pos; - F32 fvar = current_tweak->missle.hit_dist + haz.custdata.typical.rad_cur; + void cruise_bubble::state_missle_explode::cb_droplet(zFrag* frag, zFragAsset* asset) + { + F32 rand; + F32 x, y, z, w; + xVec3 vx, vy; - // scheduling for implicit copy ctor off - if (vvar.length2() < fvar * fvar) - { - ((NPCHazard**)context)[0] = &haz; - return false; - } + frag->info.projectile.fasset->flags |= 0x22; + // This obviously doesn't make sense. I'm guessing vy should + // be assigned to the result of perturb_direction before vx + // is assigned to vy, but can't get that to work because the + // operator= gets inserted. As for state, I have no idea. Not + // to mention the result of get_next_quadrant not being used. + get_next_quadrant(x, y, z, w); + state_missle_explode* state = (state_missle_explode*)&vy; + state->perturb_direction(shared.hit_norm, x, y, z, w); - return true; -} + *(S32*)(&vx.x) = *(S32*)(&vy.x); + *(S32*)(&vx.y) = *(S32*)(&vy.y); + *(S32*)(&vx.z) = *(S32*)(&vy.z); -U8 cruise_bubble::state_missle_fly::collide() -{ - xVec3* hit_norm = &shared.hit_norm; - xVec3* hit_loc = &shared.hit_loc; - xEnt* hit_ent; - xVec3 hit_depen; - S32 i = 0; + rand = xpow(xurand(), 1.0f / 3.0f); - do - { - if (!this->hit_test(*hit_loc, *hit_norm, hit_depen, hit_ent)) - { - return false; + frag->info.projectile.path.initPos = get_missle_mat()->pos + (vx * ((rand * (current_tweak->droplet.dist_max - current_tweak->droplet.dist_min) + current_tweak->droplet.dist_min))); + frag->info.projectile.path.initVel = vx * (((current_tweak->droplet.vel_max - current_tweak->droplet.vel_min) * xurand() + current_tweak->droplet.vel_min)); + frag->info.projectile.path.initVel.x = current_tweak->droplet.vel_perturb * (xurand() - 0.5f) + frag->info.projectile.path.initVel.x; + frag->info.projectile.path.initVel.y = current_tweak->droplet.vel_perturb * (xurand() - 0.5f) + frag->info.projectile.path.initVel.y; + frag->info.projectile.path.initVel.z = current_tweak->droplet.vel_perturb * (xurand() - 0.5f) + frag->info.projectile.path.initVel.z; + frag->info.projectile.angVel = current_tweak->droplet.rot_vel_max * xurand(); + frag->info.projectile.alpha = 0.25f; } - if (cruise_bubble::can_damage(hit_ent)) + void cruise_bubble::state_missle_explode::apply_damage_hazards(F32 param) { - damage_entity(*hit_ent, *hit_loc, cruise_bubble::get_missle_mat()->at, *hit_norm, - zEntCruiseBubble_f_0_0, false); - return true; + HAZ_Iterate(&hazard_check, *(void**)¶m, 0x208000); } - xMat4x3* mat = cruise_bubble::get_missle_mat(); - F32 ang = xasin(mat->at.dot(*hit_norm)); - if (ang < -current_tweak->missle.crash_angle) + cruise_bubble::state_missle_explode::cb_damage_ent::cb_damage_ent(F32 radius) { - return true; + this->radius = radius; } - mat->pos += hit_depen; - xVec3 diff = mat->pos - this->last_loc; - F32 len = diff.length2(); - if (len < zEntCruiseBubble_f_0_001) + void cruise_bubble::state_missle_explode::stop() { - return false; + shared.flags &= 0xffffffbf; } - len = xsqrt(len); - xVec3 diff_normalized = diff * (zEntCruiseBubble_f_1_0 / len); - F32 sin = -xasin(diff_normalized.y); - if (F32(iabs(sin)) > zEntCruiseBubble_f_1_5708 * current_tweak->missle.fly.turn.ybound) + cruise_bubble::state_enum cruise_bubble::state_missle_explode::update(F32 dt) { - return true; - } + this->hit_time += dt; - F32 tan = xatan2(diff_normalized.x, diff_normalized.z); - F32 mod = - xrmod(zEntCruiseBubble_f_3_1415 + (tan - this->rot.x)) - zEntCruiseBubble_f_3_1415; - this->rot.x += mod * current_tweak->missle.collide_twist; - this->rot.y += (sin - this->rot.y) * current_tweak->missle.collide_twist; - xMat3x3Euler(mat, &this->rot); + if (this->hit_time <= current_tweak->missle.explode.hit_duration) + { + F32 t = this->hit_time / current_tweak->missle.explode.hit_duration; + this->apply_damage(t * current_tweak->missle.explode.hit_radius); + } - if (hit_depen.length2() < zEntCruiseBubble_f_0_000001) - { - return false; + return STATE_MISSLE_EXPLODE; } - } while (++i < current_tweak->missle.hit_tests); - F32 dist = zEntCruiseBubble_f_0_1 * current_tweak->missle.hit_dist; - if (hit_depen.length2() < dist * dist) - { - return false; - } + void cruise_bubble::state_camera_aim::start() + { + capture_camera(); + xSndSelectListenerMode(SND_LISTENER_MODE_CAMERA); + xCameraUpdate(&globals.camera, 0.001f); - return this->hit_test(*hit_loc, *hit_norm, hit_depen, hit_ent); -} + this->phi_vel = this->height_vel = this->dist_vel = 0.0f; + xMat4x3* mat = &globals.camera.mat; + xVec3& ploc = get_player_loc(); + F32 x = mat->pos.x - ploc.x; + F32 y = mat->pos.z - ploc.z; + xVec2 offset = {}; + offset.x = x; + offset.y = y; -U8 cruise_bubble::state_missle_fly::hit_test(xVec3& hit_loc, xVec3& hit_norm, xVec3& hit_depen, - xEnt*& hit_ent) const -{ - xScene* s = globals.sceneCur; - xVec3* loc = &get_missle_mat()->pos; - xSweptSphere ss; - xSweptSpherePrepare(&ss, (xVec3*)&this->last_loc, loc, current_tweak->missle.hit_dist); - ss.optr = NULL; - if (!xSweptSphereToScene(&ss, s, NULL, 0x10)) - { - return false; - } + this->phi = xatan2(offset.x, offset.y); + this->height = mat->pos.y - ploc.y; + this->dist = offset.length(); + this->control_delay = this->seize_delay = 0.0f; - xSweptSphereGetResults(&ss); - // scheduling off - xVec3 overshoot = zEntCruiseBubble_xVec3_0_0_0; - overshoot.x = loc->x - ss.worldPos.x; - overshoot.y = loc->y - ss.worldPos.y; - overshoot.z = loc->z - ss.worldPos.z; - // till here - hit_loc = ss.worldPos + ss.worldTangent * overshoot.dot(ss.worldTangent); - hit_depen = hit_loc - *loc; - hit_norm = ss.worldNormal; - hit_ent = (xEnt*)ss.optr; - - return true; -} + xQuatFromMat(&this->facing, mat); + start_cam_mat = *mat; + start_cam_mat.pos -= ploc; + } -void cruise_bubble::state_missle_fly::update_move(F32 dt) -{ - F32 accel = current_tweak->missle.fly.accel; - F32 max = current_tweak->missle.fly.max_vel; - F32 move = zEntCruiseBubble_f_0_0; + void cruise_bubble::state_camera_aim::stop() + { + release_camera(); + } - xAccelMove(move, this->vel, accel, dt, max); + cruise_bubble::state_enum cruise_bubble::state_camera_aim::update(F32 dt) + { + this->control_delay += dt; + bool control = false; + if ((shared.flags & 0x20) && this->control_delay >= current_tweak->aim_delay) + { + control = true; + } - xMat4x3* mat = get_missle_mat(); - mat->pos += mat->at * move; -} + if (control) + { + this->move(dt); + } + else + { + this->stop(dt); + } -void cruise_bubble::state_missle_explode::start() -{ - shared.flags |= 0x40; - // scheduling for zEntCruiseBubble_f_0_0 - this->hit_time = 0.0f; + this->apply_motion(); + this->collide_inward(); + this->turn(dt); + this->apply_turn(); - if (current_tweak->missle.explode.hit_duration <= 0.0f) - this->apply_damage(current_tweak->missle.explode.hit_radius); + if (shared.flags & 0x8) + { + this->seize_delay += dt; + if (this->seize_delay >= current_tweak->camera.seize.delay) + { + return STATE_CAMERA_SEIZE; + } + } + return STATE_CAMERA_AIM; + } - F32 dist = (shared.hit_loc - get_player_loc()).length2(); - F32 mul = current_tweak->camera.survey.min_dist * current_tweak->camera.survey.min_dist; - F32 min_dist = mul; - set_state(THREAD_CAMERA, dist <= min_dist ? STATE_CAMERA_RESTORE : STATE_CAMERA_SURVEY); + void cruise_bubble::state_camera_aim::apply_turn() const + { + xMat3x3 mat; - play_sound(1, 1.0f, &get_missle_mat()->pos); - this->start_effects(); -} + xQuatToMat(&this->facing, &mat); + xCameraRotate(&globals.camera, mat, 0.0f, 0.0f, 0.0f); + } -void cruise_bubble::state_missle_explode::stop() -{ - shared.flags &= 0xffffffbf; -} + void cruise_bubble::state_camera_aim::stop(F32 dt) + { + xAccelStop(this->height, this->height_vel, current_tweak->camera.aim.accel, dt); + xAccelStop(this->dist, this->dist_vel, current_tweak->camera.aim.accel, dt); + xAccelStop(this->phi, this->phi_vel, current_tweak->camera.aim.stick_decel, dt); -cruise_bubble::state_enum cruise_bubble::state_missle_explode::update(F32 dt) -{ - this->hit_time += dt; + this->phi = xrmod(this->phi); + } - if (this->hit_time <= current_tweak->missle.explode.hit_duration) - { - F32 t = this->hit_time / current_tweak->missle.explode.hit_duration; - this->apply_damage(t * current_tweak->missle.explode.hit_radius); - } + void cruise_bubble::state_camera_seize::start() + { + capture_camera(); - return STATE_MISSLE_EXPLODE; -} + this->blend_time = 0.0f; + this->start_loc = globals.camera.mat.pos; + xQuatFromMat(&this->start_dir, &globals.camera.mat); + this->last_s = 0.0f; + this->fov = 0.0f; + this->wipe_bubbles = 0.0f; -void cruise_bubble::state_camera_aim::start() -{ - capture_camera(); - xSndSelectListenerMode(SND_LISTENER_MODE_CAMERA); - xCameraUpdate(&globals.camera, zEntCruiseBubble_f_0_001); - - this->phi_vel = this->height_vel = this->dist_vel = zEntCruiseBubble_f_0_0; - xMat4x3* mat = &globals.camera.mat; - xVec3& ploc = get_player_loc(); - // pretty sure the next 5 lines were originally just 1 looking like this: - // xVec2 offset = {mat->pos.x - ploc.x, mat->pos.z - ploc.z}; - // this will however call the implicit copy constructor with a generated instance - // in the .sbss2 section (lbl_803D0830) - // writing it like this narrowed the diff down to only a few - // regalloc and scheduling issues - F32 x = mat->pos.x - ploc.x; - F32 y = mat->pos.z - ploc.z; - xVec2 offset = lbl_803D0830; - offset.x = x; - offset.y = y; - - this->phi = xatan2(offset.x, offset.y); - this->height = mat->pos.y - ploc.y; - this->dist = offset.length(); - this->control_delay = this->seize_delay = zEntCruiseBubble_f_0_0; - - xQuatFromMat(&this->facing, mat); - start_cam_mat = *mat; - start_cam_mat.pos -= ploc; -} + show_hud(); + distort_screen(0.0f); + } -void cruise_bubble::state_camera_aim::stop() -{ - release_camera(); -} + void cruise_bubble::state_camera_seize::stop() + { + release_camera(); + xCameraSetFOV(&globals.camera, shared.fov_default); + } -cruise_bubble::state_enum cruise_bubble::state_camera_aim::update(F32 dt) -{ - this->control_delay += dt; - bool control = false; - if ((shared.flags & 0x20) != 0 && this->control_delay >= current_tweak->aim_delay) - { - control = true; - } + cruise_bubble::state_enum cruise_bubble::state_camera_seize::update(F32 dt) + { + this->blend_time += dt; + if (this->blend_time >= current_tweak->camera.seize.blend_time) + { + return STATE_CAMERA_ATTACH; + } - if (control) - { - this->move(dt); - } - else - { - this->stop(dt); - } + F32 time_frac = this->blend_time / current_tweak->camera.seize.blend_time; + F32 s = xSCurve(time_frac); + this->update_move(s); + this->update_turn(s); + xVec3 offset = get_missle_mat()->pos - globals.camera.mat.pos; + this->refresh_missle_alpha(offset.length()); - this->apply_motion(); - this->collide_inward(); - this->turn(dt); - this->apply_turn(); + F32 dist = current_tweak->camera.seize.fov - shared.fov_default; + xCameraSetFOV(&globals.camera, s * dist + shared.fov_default); + distort_screen(s); - if ((shared.flags & 0x8) != 0) - { - this->seize_delay += dt; - if (this->seize_delay >= current_tweak->camera.seize.delay) - { return STATE_CAMERA_SEIZE; } - } - return STATE_CAMERA_AIM; -} - -void cruise_bubble::state_camera_aim::apply_turn() const -{ - xMat3x3 mat; - xQuatToMat(&this->facing, &mat); - xCameraRotate(&globals.camera, mat, 0.0f, 0.0f, 0.0f); -} - -void cruise_bubble::state_camera_seize::start() -{ - capture_camera(); - - this->blend_time = zEntCruiseBubble_f_0_0; - this->start_loc = globals.camera.mat.pos; - xQuatFromMat(&this->start_dir, &globals.camera.mat); - this->last_s = zEntCruiseBubble_f_0_0; - this->fov = zEntCruiseBubble_f_0_0; - this->wipe_bubbles = zEntCruiseBubble_f_0_0; + void cruise_bubble::state_camera_seize::refresh_missle_alpha(F32 dt) + { + F32 fade = current_tweak->camera.seize.fade_dist; + F32 hide = current_tweak->camera.seize.hide_dist; + if (dt >= fade) + { + show_missle(); + shared.missle_model->Alpha = 1.0f; + } + else if (dt <= hide) + { + hide_missle(); + shared.missle_model->Flags |= 2; + } + else + { + dt = hide - dt; + F32 dx = hide - fade; + shared.missle_model->Alpha = dt / dx; + } + } - show_hud(); - distort_screen(zEntCruiseBubble_f_0_0); -} + void cruise_bubble::state_camera_attach::start() + { + capture_camera(); + xMat4x3* mat = get_missle_mat(); + xCameraMove(&globals.camera, mat->pos); + xCameraRotate(&globals.camera, *mat, 0.0f, 0.0f, + 0.0f); + xCameraSetFOV(&globals.camera, current_tweak->camera.seize.fov); + distort_screen(1.0f); + } -void cruise_bubble::state_camera_seize::stop() -{ - release_camera(); - xCameraSetFOV(&globals.camera, shared.fov_default); -} + void cruise_bubble::state_camera_attach::stop() + { + xCameraSetFOV(&globals.camera, shared.fov_default); + release_camera(); + hide_hud(); + distort_screen(0.0f); + } -void cruise_bubble::state_camera_seize::refresh_missle_alpha(F32 dt) -{ - F32 fade = current_tweak->camera.seize.fade_dist; - F32 hide = current_tweak->camera.seize.hide_dist; - if (dt >= fade) - { - show_missle(); - shared.missle_model->Alpha = 1.0f; - } - else if (dt <= hide) - { - hide_missle(); - shared.missle_model->Flags |= 2; - } - else - { - dt = hide - dt; - F32 dx = hide - fade; - shared.missle_model->Alpha = dt / dx; - } -} + cruise_bubble::state_enum cruise_bubble::state_camera_attach::update(F32 dt) + { + xMat4x3* mat = get_missle_mat(); + xCameraMove(&globals.camera, mat->pos); + xCameraRotate(&globals.camera, *mat, 0.0f, 0.0f, 0.0f); + this->lock_targets(); -cruise_bubble::state_enum cruise_bubble::state_camera_seize::update(F32 dt) -{ - this->blend_time += dt; - if (this->blend_time >= current_tweak->camera.seize.blend_time) - { - return STATE_CAMERA_ATTACH; - } + return STATE_CAMERA_ATTACH; + } - F32 time_frac = this->blend_time / current_tweak->camera.seize.blend_time; - F32 s = xSCurve(time_frac); - this->update_move(s); - this->update_turn(s); - xVec3 offset = get_missle_mat()->pos - globals.camera.mat.pos; - this->refresh_missle_alpha(offset.length()); + void cruise_bubble::state_camera_attach::lock_targets() + { + cb_lock_targets targets; + xBound bound; - F32 dist = current_tweak->camera.seize.fov - shared.fov_default; - xCameraSetFOV(&globals.camera, s * dist + shared.fov_default); - distort_screen(s); + get_view_bound(bound); + xGridCheckBound(colls_grid, bound, bound.qcd, targets); + xGridCheckBound(colls_oso_grid, bound, bound.qcd, targets); + xGridCheckBound(npcs_grid, bound, bound.qcd, targets); + lock_hazard_targets(); + } - return STATE_CAMERA_SEIZE; -} + void cruise_bubble::state_camera_attach::lock_hazard_targets() + { + HAZ_Iterate(hazard_check, 0, 0x208000); + } -void cruise_bubble::state_camera_attach::start() -{ - capture_camera(); - xMat4x3* mat = get_missle_mat(); - xCameraMove(&globals.camera, mat->pos); - xCameraRotate(&globals.camera, *mat, zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_0_0, - zEntCruiseBubble_f_0_0); - xCameraSetFOV(&globals.camera, current_tweak->camera.seize.fov); - distort_screen(zEntCruiseBubble_f_1_0); -} + bool cruise_bubble::state_camera_attach::hazard_check(NPCHazard& haz, void* context) + { + check_lock_target(&haz.pos_hazard); + return 1; + } -void cruise_bubble::state_camera_attach::stop() -{ - xCameraSetFOV(&globals.camera, shared.fov_default); - release_camera(); - hide_hud(); - distort_screen(zEntCruiseBubble_f_0_0); -} + void cruise_bubble::state_camera_survey::start() + { + if (camera_taken()) + { + release_camera(); + this->time = current_tweak->camera.survey.duration; + return; + } -U8 cruise_bubble::state_camera_attach::hazard_check(NPCHazard& haz, void* context) -{ - check_lock_target(&haz.pos_hazard); - return 1; -} + capture_camera(); + this->time = 0.0f; + this->init_path(); + this->move(); + this->start_sp = shared.sp; + } -cruise_bubble::state_enum cruise_bubble::state_camera_attach::update(F32 dt) -{ - xMat4x3* mat = get_missle_mat(); - xCameraMove(&globals.camera, mat->pos); - xCameraRotate(&globals.camera, *mat, zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_0_0, - zEntCruiseBubble_f_0_0); - this->lock_targets(); + void cruise_bubble::state_camera_survey::lerp(F32& a, F32 b, F32 c, F32 d) const + { + a = (b * (d - c)) + c; + } - return STATE_CAMERA_ATTACH; -} + void cruise_bubble::state_camera_survey::lerp(xVec3& a, F32 b, const xVec3& c, const xVec3& d) const + { + lerp(a.x, b, c.x, d.x); + lerp(a.y, b, c.y, d.y); + lerp(a.z, b, c.z, d.z); + } -void cruise_bubble::state_camera_survey::start() -{ - if (camera_taken()) - { - release_camera(); - this->time = current_tweak->camera.survey.duration; - return; - } + void cruise_bubble::state_camera_survey::stop() + { + release_camera(); + } - capture_camera(); - this->time = zEntCruiseBubble_f_0_0; - this->init_path(); - this->move(); - this->start_sp = shared.sp; -} + cruise_bubble::state_enum cruise_bubble::state_camera_survey::update(F32 dt) + { + this->time += dt; -void cruise_bubble::state_camera_survey::stop() -{ - release_camera(); -} + if (camera_taken()) + { + return STATE_CAMERA_RESTORE; + } -void cruise_bubble::state_camera_survey::lerp(F32& a, F32 b, F32 c, F32 d) const -{ - a = (b * (d - c)) + c; -} + if (this->time >= current_tweak->camera.survey.duration) + { + return STATE_CAMERA_RESTORE; + } -void cruise_bubble::state_camera_survey::lerp(xVec3& a, F32 b, const xVec3& c, const xVec3& d) const -{ - lerp(a.x, b, c.x, d.x); - lerp(a.y, b, c.y, d.y); - lerp(a.z, b, c.z, d.z); -} + if (this->time >= current_tweak->camera.survey.min_duration && + ((globals.pad0->pressed & 0x100) || this->control_jerked())) + { + return STATE_CAMERA_RESTORE; + } -cruise_bubble::state_enum cruise_bubble::state_camera_survey::update(F32 dt) -{ - this->time += dt; + this->move(); + return STATE_CAMERA_SURVEY; + } - if (camera_taken()) - { - return STATE_CAMERA_RESTORE; - } + void cruise_bubble::state_camera_restore::start() + { + this->control_delay = 0.0f; + hide_hud(); - if (this->time >= current_tweak->camera.survey.duration) - { - return STATE_CAMERA_RESTORE; - } + if (!camera_leave()) + { + xVec3 loc = get_player_loc() + start_cam_mat.pos; + xCameraMove(&globals.camera, loc); + xCameraRotate(&globals.camera, start_cam_mat, 0.0f, + 0.0f, 0.0f); + } - if (this->time >= current_tweak->camera.survey.min_duration && - ((globals.pad0->pressed & 0x100) != 0 || this->control_jerked())) - { - return STATE_CAMERA_RESTORE; - } + if (camera_taken()) + { + release_camera(); + } + else + { + capture_camera(); + } - this->move(); - return STATE_CAMERA_SURVEY; -} + xSndSelectListenerMode(SND_LISTENER_MODE_PLAYER); + } -void cruise_bubble::state_camera_restore::start() -{ - this->control_delay = zEntCruiseBubble_f_0_0; - hide_hud(); + void cruise_bubble::state_camera_restore::stop() + { + cruise_bubble::set_state(THREAD_PLAYER, STATE_INVALID); + release_camera(); + } - if (!camera_leave()) - { - xVec3 loc = get_player_loc() + start_cam_mat.pos; - xCameraMove(&globals.camera, loc); - xCameraRotate(&globals.camera, start_cam_mat, zEntCruiseBubble_f_0_0, - zEntCruiseBubble_f_0_0, zEntCruiseBubble_f_0_0); - } + cruise_bubble::state_enum cruise_bubble::state_camera_restore::update(F32 dt) + { + this->control_delay += dt; + if (this->control_delay >= current_tweak->camera.restore.control_delay) + { + return STATE_INVALID; + } - if (camera_taken()) - { - release_camera(); - } - else - { - capture_camera(); - } + return STATE_CAMERA_RESTORE; + } - xSndSelectListenerMode(SND_LISTENER_MODE_PLAYER); -} + S32 cruise_bubble::state_camera_attach::cb_lock_targets::operator()(xEnt& ent, xGridBound& bound) + { + if (!(ent.chkby & 0x10)) + { + return 1; + } + if (!(cruise_bubble::can_damage(&ent))) + { + return 1; + } -void cruise_bubble::state_camera_restore::stop() -{ - cruise_bubble::set_state(THREAD_PLAYER, STATE_INVALID); - release_camera(); -} + check_lock_target(xEntGetCenter(&ent)); -cruise_bubble::state_enum cruise_bubble::state_camera_restore::update(F32 dt) -{ - this->control_delay += dt; - if (this->control_delay >= current_tweak->camera.restore.control_delay) - { - return STATE_INVALID; - } + return 1; + } - return STATE_CAMERA_RESTORE; -} + missle_record_data::missle_record_data() + { + } + } // namespace +} // namespace cruise_bubble S32 zNPCCommon::IsHealthy() { diff --git a/src/SB/Game/zEntCruiseBubble.h b/src/SB/Game/zEntCruiseBubble.h index 7a9c62b2a..db62d1475 100644 --- a/src/SB/Game/zEntCruiseBubble.h +++ b/src/SB/Game/zEntCruiseBubble.h @@ -131,6 +131,13 @@ namespace cruise_bubble { F32 hit_time; + struct cb_damage_ent + { + F32 radius; + + cb_damage_ent(F32 radius); + }; + state_missle_explode(); void start(); @@ -139,17 +146,22 @@ namespace cruise_bubble F32 get_radius() const; void start_effects(); - void cb_droplet(zFrag* frag, zFragAsset* fa); + static void cb_droplet(zFrag* frag, zFragAsset* fa); void perturb_direction(const xVec3&, F32, F32, F32, F32); - void get_next_quadrant(F32&, F32&, F32&, F32&); + static void get_next_quadrant(F32&, F32&, F32&, F32&); void reset_quadrants(U32 size, F32 ring); void apply_damage(F32 radius); void apply_damage_hazards(F32); - U8 hazard_check(NPCHazard& haz, void* context); + static bool hazard_check(NPCHazard& haz, void* context); }; struct state_camera_attach : state_type { + struct cb_lock_targets + { + S32 operator()(xEnt&, xGridBound&); + }; + F32 reticle_delay; state_camera_attach(); @@ -160,7 +172,7 @@ namespace cruise_bubble void lock_targets(); void lock_hazard_targets(); - static U8 hazard_check(NPCHazard& haz, void* context); + static bool hazard_check(NPCHazard& haz, void* context); void get_view_bound(xBound& bound) const; }; @@ -168,6 +180,7 @@ namespace cruise_bubble { F32 life; F32 vel; + // Offset: 0x10 xVec3 rot; // Offset: 0x1c xVec3 rot_vel; @@ -291,15 +304,15 @@ namespace cruise_bubble { F32 aim_delay; // Size: 0x10, Offset: 0x4 - struct _class_2 + struct { F32 halt_time; // 0x4 - struct _class_4 + struct { F32 turn_speed; // 0x8 F32 anim_delta; // 0xC } aim; - struct _class_11 + struct { F32 delay_wand; // 0x10 } fire; @@ -313,13 +326,13 @@ namespace cruise_bubble F32 crash_angle; // 0x1c F32 collide_twist; // 0x20 S32 hit_tests; // 0x24 - struct _class_27 + struct { F32 delay_show; // 0x28 F32 delay_fly; // 0x2c xVec3 offset; // 0x30 } appear; - struct _class_32 + struct { F32 accel; // 0x3c F32 max_vel; // 0x40 @@ -327,7 +340,7 @@ namespace cruise_bubble F32 engine_pitch_sensitivity; // 0x48 // Offset: 0x4c F32 flash_interval; - struct _class_38 + struct { F32 xdelta; // 0x50 F32 ydelta; // 0x54 @@ -337,7 +350,7 @@ namespace cruise_bubble F32 roll_frac; // 0x64 } turn; } fly; - struct _class_49 + struct { F32 hit_radius; // 0x68 F32 hit_duration; // 0x6c @@ -345,9 +358,9 @@ namespace cruise_bubble } missle; // Size: 0x5c, Offset: 0x70 - struct _class_10 + struct { - struct _class_15 + struct { F32 dist; F32 height; // 0x74 @@ -359,7 +372,7 @@ namespace cruise_bubble F32 stick_max_vel; // 0x8c F32 turn_speed; // 0x90 } aim; - struct _class_24 + struct { F32 delay; // 0x94 F32 blend_time; // 0x98 @@ -367,7 +380,7 @@ namespace cruise_bubble F32 hide_dist; // 0xa0 F32 fov; // 0xa4 } seize; - struct _class_30 + struct { F32 duration; // 0xa8 F32 min_duration; // 0xac @@ -378,14 +391,14 @@ namespace cruise_bubble F32 jerk_offset; // 0xc0 F32 jerk_deflect; // 0xc4 } survey; - struct _class_39 + struct { F32 control_delay; // 0xc8 } restore; } camera; // Size: 0x18, Offset: 0xcc - struct _class_48 + struct { F32 env_alpha; F32 env_coeff; // 0xd0 @@ -396,7 +409,7 @@ namespace cruise_bubble } material; // Size: 0x14, Offset: 0xe4 - struct _class_9 + struct { // Offset: 0xe4 F32 dist_min; @@ -408,7 +421,7 @@ namespace cruise_bubble } reticle; // Size: 0x10, Offset: 0xf8 - struct _class_20 + struct { // Offset: 0xf8 F32 sample_rate; @@ -418,7 +431,7 @@ namespace cruise_bubble } trail; // Size: 0x10, Offset: 0x108 - struct _class_29 + struct { U32 emit; // 0x108 F32 radius; // 0x10c @@ -427,7 +440,7 @@ namespace cruise_bubble } blast; // Size: 0x24, Offset: 0x118 - struct _class_35 + struct { F32 dist_min; F32 dist_max; @@ -441,33 +454,33 @@ namespace cruise_bubble } droplet; // Size: 0x44, Offset: 0x13c - struct _class_43 + struct { F32 glow_size; // Offset: 0x140 F32 time_fade; F32 time_glow; - struct _class_46 + struct { F32 size; F32 du; F32 dv; } swirl; - struct _class_5 + struct { F32 size; F32 du; F32 dv; } wind; - struct _class_12 + struct { F32 size; } reticle; - struct _class_18 + struct { F32 size; } target; - struct _class_23 + struct { // Offset: 0x168 S32 font; @@ -481,7 +494,7 @@ namespace cruise_bubble } hud; // Size: 0xc, Offset: 0x180 - struct _class_34 + struct { F32 freq; F32 decay; @@ -555,90 +568,95 @@ namespace cruise_bubble xVec3 loc; F32 roll; + missle_record_data(); missle_record_data(const xVec3& loc, F32 roll); }; + + RpAtomic* custom_bubble_render(RpAtomic* atomic); + void reset_wake_ribbons(); + xMat4x3* get_missle_mat(); + void render_model_2d(xModelInstance* m, const basic_rect& bound, F32 alpha); + void render_glow(xModelInstance* m, const basic_rect& r, F32 glow, F32 alpha); + void load_cheat_tweak(); + void refresh_missle_model(); + void hide_wand(); + xMat4x3* get_player_mat(); + U8 can_damage(xEnt* ent); + void kill(bool reset_camera, bool abortive); + void render_player(); + void render_state(); + void render_missle(); + void render_debug(); + void render_hud(); + void show_hud(); + void hide_hud(); + void set_state(thread_enum thread, state_enum state); + void start_damaging(); + void show_missle(); + void refresh_trail(xMat4x3& mat, xQuat& quat); + void add_trail_sample(const xVec3& loc0, const xVec3& dir0, const xVec3& loc1, + const xVec3& dir1, F32 dt); + bool check_launch(); + void refresh_controls(); + void update_state(xScene* s, F32 dt); + void update_player(xScene& s, F32 dt); + void update_missle(xScene& s, F32 dt); + void update_hud(F32 dt); + void lerp(iColor_tag& c, F32 t, iColor_tag a, iColor_tag b); + void lerp(U8& x, F32 t, U8 a, U8 b); + xVec3 world_to_screen(const xVec3& loc); + S32 find_locked_target(const xVec3* target); + void lock_target(S32 index, const xVec3* target, F32 opacity); + void load_settings(); + void init_states(); + void init_missle_model(); + void init_wake_ribbons(); + void reset_explode_decal(); + void init_explode_decal(); + void init_shrapnel(); + void init_hud(); + void show_gizmo(hud_gizmo& gizmo, const basic_rect& rect, xModelInstance* m); + void update_gizmo(hud_gizmo& gizmo, F32 dt); + void flash_hud(); + void init_debug(); + void init_sound(); + U32 play_sound(S32 which, F32 volFactor); + U32 play_sound(S32 which, F32 volFactor, const xVec3* pos); + void distort_screen(F32); + xVec3& get_player_loc(); + void stop_sound(S32 which, U32 handle); + void set_pitch(S32 which, F32 pitch, U32 handle); + void show_wand(); + void hide_missle(); + void capture_camera(); + void release_camera(); + bool camera_taken(); + bool camera_leave(); + // void damage_entity(xEnt& ent, const xVec3& loc, const xVec3& dir, const xVec3& hit_norm, + // F32 radius, bool explosive); + U8 was_damaged(xEnt* ent); + void notify_triggers(xScene& s, const xSphere& o, const xVec3& dir); + void exit_triggers(xScene& s); + void signal_event(U32 toEvent); + void start_trail(); + void stop_trail(); + void update_trail(F32 dt); + xModelInstance* load_model(U32); + void render_timer(F32 alpha, F32 glow); + void check_lock_target(const xVec3* target); + U32 check_anim_aim(xAnimTransition*, xAnimSingle*); } // namespace - void init_sound(); - void stop_sound(S32 which, U32 handle); - U32 play_sound(S32 which, F32 volFactor); - U32 play_sound(S32 which, F32 volFactor, const xVec3* pos); - void set_pitch(S32 which, F32 pitch, U32 handle); - void show_wand(); - void hide_wand(); - void show_missle(); - void hide_missle(); - void capture_camera(); - void release_camera(); - bool camera_taken(); - bool camera_leave(); - void start_damaging(); - // void damage_entity(xEnt& ent, const xVec3& loc, const xVec3& dir, const xVec3& hit_norm, - // F32 radius, bool explosive); - U8 can_damage(xEnt* ent); - U8 was_damaged(xEnt* ent); - void notify_triggers(xScene& s, const xSphere& o, const xVec3& dir); - void exit_triggers(xScene& s); - void signal_event(U32 toEvent); - void refresh_trail(xMat4x3& mat, xQuat& quat); - void start_trail(); - void stop_trail(); - void set_state(thread_enum thread, state_enum state); - bool check_launch(); - void kill(bool reset_camera, bool abortive); - void distort_screen(F32); - void update_player(xScene& s, F32 dt); - xVec3& get_player_loc(); - void render_player(); - void refresh_controls(); - void update_state(xScene* s, F32 dt); - void render_state(); - RpAtomic* custom_bubble_render(RpAtomic* atomic); - void init_states(); - void init_missle_model(); - void reset_wake_ribbons(); - void init_wake_ribbons(); - void reset_explode_decal(); - void init_explode_decal(); - void init_shrapnel(); - void add_trail_sample(const xVec3& loc0, const xVec3& dir0, const xVec3& loc1, - const xVec3& dir1, F32 dt); - void update_trail(F32 dt); - void refresh_missle_model(); - void update_missle(xScene& s, F32 dt); - void render_missle(); - xModelInstance* load_model(U32); - void render_model_2d(xModelInstance* m, const basic_rect& bound, F32 alpha); - void render_glow(xModelInstance* m, const basic_rect& r, F32 glow, F32 alpha); - void init_hud(); - void show_gizmo(hud_gizmo& gizmo, const basic_rect& rect, xModelInstance* m); - void update_gizmo(hud_gizmo& gizmo, F32 dt); - void flash_hud(); - void render_timer(F32 alpha, F32 glow); - void lerp(iColor_tag& c, F32 t, iColor_tag a, iColor_tag b); - void lerp(U8& x, F32 t, U8 a, U8 b); - void update_hud(F32 dt); - void render_hud(); - void show_hud(); - void hide_hud(); - xVec3 world_to_screen(const xVec3& loc); - S32 find_locked_target(const xVec3* target); - void lock_target(S32 index, const xVec3* target, F32 opacity); - void check_lock_target(const xVec3* target); - U32 check_anim_aim(xAnimTransition*, xAnimSingle*); - void load_cheat_tweak(); - void load_settings(); void init(); - void init_debug(); void reset(); void launch(); bool update(xScene* s, F32 dt); bool render(); - void render_debug(); void render_screen(); void insert_player_animations(xAnimTable& table); - bool active(); + xAnimTable* anim_table(); F32 exploding(); + bool active(); void get_explode_sphere(xVec3& center, F32& radius); xEnt** get_explode_hits(S32& size); void add_life(F32, F32); @@ -646,9 +664,6 @@ namespace cruise_bubble void reset_life(); // xBase* param names are guessed as they go unused and wont appear in dwarf bool event_handler(xBase* from, U32 event, const F32* fparam, xBase* to); - xMat4x3* get_player_mat(); - xMat4x3* get_missle_mat(); - xAnimTable* anim_table(); } // namespace cruise_bubble diff --git a/src/SB/Game/zFX.h b/src/SB/Game/zFX.h index 35ff9442b..5b181a1d2 100644 --- a/src/SB/Game/zFX.h +++ b/src/SB/Game/zFX.h @@ -72,6 +72,7 @@ S32 zFXGooIs(xEnt* obj, F32& depth, U32 playerCheck); void zFX_SpawnBubbleHit(const xVec3* pos, U32 num); void zFX_SpawnBubbleWall(); void zFX_SpawnBubbleSlam(const xVec3* pos, U32 num, F32 rang, F32 bvel, F32 rvel); +void zFX_SpawnBubbleBlast(const xVec3* pos, U32 num, F32 rang, F32 bvel, F32 rvel); void reset_poppers();