Spider-Man 2: The Game (Windows, Mac OS X)/Unused & Commented-Out Code
This is a sub-page of Spider-Man 2: The Game (Windows, Mac OS X).
This code can be found in Webhead.u of the game ported to Mac. There's a lot of "documentation" comments + many commented-out log calls, but they aren't that interesting.
What's interesting is the following:
Contents
Movie Ticket
Full code of those unused MovieTicket collectables. The function TicketPickedUp is in a different class and it's fully commented-out. That's why tickets don't give you anything.
class MovieTicket extends Actor; var Emitter AmbientFX; var class<Emitter> PickupFXClass; var class<Emitter> AmbientFXClass; var sound PickupSound; var Actor PullToward; var float PullAccel; event Destroyed() { AmbientFX.Destroy(); super.Destroyed(); } function PostBeginPlay() { super.PostBeginPlay(); AmbientFX = Spawn( AmbientFXClass, self); AmbientFX.SetPhysics(PHYS_Trailer); } function PickedUp() { MiniChallenge(Owner).TicketPickedUp(); Spawn( PickupFXClass ); } function Touch( Actor other ) { if ( other.IsA('SpideyPawn') ) { PickedUp(); AmbientFX.Destroy(); Destroy(); } } function Trigger( actor Other, pawn EventInstigator ) { // this should be a web pull if ( Other.IsA('SpideyPawn') && EventInstigator.IsA('SpideyPawn') ) { // move toward spidey until you hit him PullToward = Other; SetTimer( 0.01f, true ); } } function Timer() { local Vector Direction; local float distance; distance = VSize(PullToward.Location - Location); if ( distance > CollisionRadius ) { Direction = Normal( PullToward.Location - Location ); SetLocation( Location + Direction * PullAccel); } else SetLocation( PullToward.Location ); }
MiniChallenge class has commented-out MovieTicket functionality in it. It proves, that MiniChallenges were initially a way to get these movie tickets!
// if this is a ticket emitter, then see if this ticket has already been awarded //if ( Reward == Reward_Ticket ) //{ // foreach allactors(class'GameState', gs ) // break; // gs.GetState( Tag, val ); // if ( val == 1 ) // { // log("MovieTicket already picked up "$Tag ); // bSolvedOnce = true; // bOneTime = true; // } //} //case Reward_Ticket: // Spawn( class'MovieTicket', self, , Location + GoodyOffset ); // showEffect = true; // break; function TicketPickedUp() { //WebheadGameInfo(Level.Game).States.SetState( Tag, 1 ); }
Page Movie Stills
This class is 100% unused, since the devs left Movie Tickets unused. The way to use it in the game wasn't yet found, but you could see this page from the Main Menu.
// ==================================================================== // Class: WebHead.PageMovieStills // Parent: Webhead.WHGUIPage // // ==================================================================== class PageMovieStills extends WebheadGUIPage; const kNumStillsPerRow = 9; const kTilesPerBlowup = 4; //----------------------------------------------- // Load texture files. //----------------------------------------------- #exec OBJ LOAD FILE=..\textures\GUIContent.utx #exec OBJ LOAD FILE=..\textures\MoviePicsOverlay.utx //----------------------------------------------- // Named reference to controls on the page. //----------------------------------------------- var automated GUIButton btnMovieStillsBkgA; var automated GUIButton btnMovieStillsBkgB; var automated GUIButton btnMovieStillsBkgC; var automated GUIButton btnMovieStillsBkgD; var automated GUIButton btnMovieStillsBkgE; var automated GUIButton btnMovieStillsBkgF; var automated GUIButton btnMovieStillsBkgG; var automated GUIButton btnMovieStillsBkgH; var automated GUIButton btnMovieStillsBkgI; var automated GUIButton btnMovieStillsBkgJ; var automated GUIButton btnMovieStillsBkgK; var automated GUIButton btnMovieStillsBkgL; var automated GUIButton btnStill00; var automated GUIButton btnStill01; var automated GUIButton btnStill02; var automated GUIButton btnStill03; var automated GUIButton btnStill04; var automated GUIButton btnStill05; var automated GUIButton btnStill06; var automated GUIButton btnStill07; var automated GUIButton btnStill08; var automated GUIButton btnStill09; var automated GUIButton btnStill10; var automated GUIButton btnStill11; var automated GUIButton btnStill12; var automated GUIButton btnStill13; var automated GUIButton btnStill14; var automated GUIButton btnStill15; var automated GUIButton btnStill16; var automated GUIButton btnStill17; var automated GUIBUtton btnStill[18]; var automated GUIButton btnBlowup0; var automated GUIButton btnBlowup1; var automated GUIButton btnBlowup2; var automated GUIButton btnBlowup3; var automated GUIButton btnBlowup[4]; var automated GUIButton btnDone; var automated GUIButton btnExport; var int CurrentSelection; var int TicketCount; var bool bChanged; function InitComponent(GUIController MyController, GUIComponent MyOwner) { local GameState gameState; InitializeStills(); InitializeBlowups(); Super.InitComponent(MyController, MyOwner); ForEach PlayerOwner().AllActors(class'GameState', gameState) { // movie stills are available only from the frontend, so must load most recent game by hand gameState.LoadLastGame(); break; } UpdateStills(); } function SetHints() { btnDone.Hint = Localize("hints", "Done", "WebheadUI"); btnExport.Hint = Localize("hints", "MovieStills_Export", "WebheadUI"); } function OnClose(optional Bool bCancelled) { local GameState gameState; if (bChanged) { ForEach PlayerOwner().AllActors(class'GameState', gameState) { gameState.Save(); break; } } Super.OnClose(bCancelled); } //----------------------------------------------------------------------------- // Click handler. // This is set up for a 'Do you want to quit' dialog, override for others. //----------------------------------------------------------------------------- function bool InternalOnClick(GUIComponent Sender) { local int i; local SpideyHud Hud; if (Sender == btnDone) { Controller.CloseMenu(false); } else if (Sender == btnExport) { if (CurrentSelection != -1) { ForEach PlayerOwner().AllActors(class'SpideyHud', Hud) { Hud.ExportMovieImage(CurrentSelection); break; } } } else { for (i = 0; i < ArrayCount(btnStill); i++) { if (Sender == btnStill[i]) { if (!Unlocked(i)) { if (GetTotalTickets() > 0) { CurrentSelection = i; Unlock(CurrentSelection); SpendTicket(); bChanged = true; } else { CurrentSelection = -1; } } else { CurrentSelection = i; } UpdateBlowup(); } } } return true; } function UpdateBlowup() { local int index, matIndex, picIndex; local string textureStr; local string tile[4]; local Material mat; tile[0] = "A"; tile[1] = "B"; tile[2] = "C"; tile[3] = "D"; for (index = 0; index < ArrayCount(btnBlowup); index++) { // invalid/no selection; hide the blowup if (CurrentSelection == -1) { btnBlowup[index].bVisible = false; } // assign the materials for each blowup style to correspond to the selected thumbnail else { picIndex = CurrentSelection + 1; textureStr = "MoviePicsOverlay.I_Op_B_LgPic"; if (picIndex < 10) textureStr = textureStr$"0"; textureStr = textureStr$picIndex$tile[index]; log("*** material update for button "$index$": "$textureStr); mat = Material(DynamicLoadObject(textureStr, class'Material')); for (matIndex = 0; matIndex < 5; matIndex++) btnBlowup[index].Style.Images[matIndex] = mat; btnBlowup[index].bVisible = true; } } } function bool OnDraw(Canvas Canvas) { local bool retval; UpdateButtons(); retval = Super.OnDraw(Canvas); TicketCount = GetTotalTickets(); DrawTicketCount(Canvas); return retval; } function UpdateButtons() { local int i; // enable/disable image buttons, export button for (i = 0; i < ArrayCount(btnStill); i++) { if (TicketCount > 0) { btnStill[i].bAcceptsInput = true; } else { if (!Unlocked(i)) btnStill[i].bAcceptsInput = false; else btnStill[i].bAcceptsInput = true; } } if (CurrentSelection >= 0) { if (Unlocked(CurrentSelection)) btnExport.bAcceptsInput = true; else btnExport.bAcceptsInput = false; } else { btnExport.bAcceptsInput = false; } } function DrawTicketCount(Canvas C) { local string text; local float XL, YL; local float posX, posY; local float initOrgX, initClipX, initOrgY, initClipY; text = ""; if ( C.ClipX <= 640 ) C.Font = MedFont; else if (C.ClipX < 1024) C.Font = BigFont; else C.Font = LargeFont; C.bCenter = false; // draw text C.SetDrawColor(255,255,255,255); // remember these initOrgX = C.OrgX; initClipX = C.ClipX; initOrgY = C.OrgY; initClipY = C.ClipY; text = text$TicketCount; C.StrLen( text, XL, YL ); C.OrgX = C.SizeX * 0.744140625 - XL*0.5; C.OrgY = C.SizeY * 0.385416666; posX = 0; // scale offsets posY = 0; // scale offsets C.SetPos(posX, posY); C.DrawText( text, false ); // reset canvas's Org, Clip values C.OrgX = initOrgX; C.ClipX = initClipX; C.OrgY = initOrgY; C.ClipY = initClipY; } function int GetTotalTickets() { local GameState gameState; ForEach PlayerOwner().AllActors(class'GameState', gameState) { return gameState.GetTicketCount(); break; } return 0; } function SpendTicket() { local GameState gameState; ForEach PlayerOwner().AllActors(class'GameState', gameState) { gameState.SpendTicket(); break; } } function Unlock(int index) { local string style; local GameState gameState; ForEach PlayerOwner().AllActors(class'GameState', gameState) { gameState.UnlockImage(index+1); break; } style = "WHMovieStill"; if (index < 10) style = style$"0"; style = style$index$"_"; btnStill[index].StyleName = style; btnStill[index].InitComponent(Controller, MenuOwner); } function bool Unlocked(int index) { local GameState gameState; ForEach PlayerOwner().AllActors(class'GameState', gameState) { return gameState.ImageUnlocked(index+1); break; } return false; } function InitializeStills() { local int i; local string style; local float leftX, topY, xOffset, yOffset; local int row, col; leftX = 0.2197265625; // 225 topY = 0.088593; // 75 xOffset = 0.0732421875; // 75 yOffset = 0.0911458333; // 70 btnStill[0] = btnStill00; btnStill[1] = btnStill01; btnStill[2] = btnStill02; btnStill[3] = btnStill03; btnStill[4] = btnStill04; btnStill[5] = btnStill05; btnStill[6] = btnStill06; btnStill[7] = btnStill07; btnStill[8] = btnStill08; btnStill[9] = btnStill09; btnStill[10] = btnStill10; btnStill[11] = btnStill11; btnStill[12] = btnStill12; btnStill[13] = btnStill13; btnStill[14] = btnStill14; btnStill[15] = btnStill15; btnStill[16] = btnStill16; btnStill[17] = btnStill17; for (i = 0; i < ArrayCount(btnStill); i++) { row = i / kNumStillsPerRow; col = i % kNumStillsPerRow; // initialize all to empty style = "WHEmpty"; btnStill[i].StyleName = style; btnStill[i].WinLeft = leftX + xOffset * col; btnStill[i].WinTop = topY + yOffset * row; btnStill[i].WinWidth = 0.0625; btnStill[i].WinHeight = 0.0833; btnStill[i].bNeverFocus = true; btnStill[i].bVisible = true; btnStill[i].bBoundToParent = true; btnStill[i].OnClick = InternalOnClick; } } function UpdateStills() { local int i; for (i = 0; i < ArrayCount(btnStill); i++) { if (Unlocked(i)) Unlock(i); } } function InitializeBlowups() { local int i; local float leftX, topY, xOffset, yOffset; local int row, col; local string tile[4]; tile[0] = "A"; tile[1] = "B"; tile[2] = "C"; tile[3] = "D"; leftX = 0.166015625; // 170 topY = 0.30078125;//0.30859375; // 237 xOffset = 0.25; // 256 yOffset = 0.33333; // 256 btnBlowup[0] = btnBlowup0; btnBlowup[1] = btnBlowup1; btnBlowup[2] = btnBlowup2; btnBlowup[3] = btnBlowup3; for (i = 0; i < ArrayCount(btnBlowup); i++) { row = i / 2; col = i % 2; // there are 4 generic styles for the blowups; UpdateBlowup() assigns the appropriate materials for each btnBlowup[i].StyleName = "WHBigPic"$tile[i]; btnBlowup[i].WinLeft = leftX + xOffset * col; btnBlowup[i].WinTop = topY + yOffset * row; btnBlowup[i].WinWidth = 0.25; btnBlowup[i].WinHeight = 0.33333; btnBlowup[i].bNeverFocus = true; btnBlowup[i].bAcceptsInput = false; btnBlowup[i].bVisible = false; btnBlowup[i].bBoundToParent = true; } }
This functionality of movie tickets and page movie stills in GameState was left unused as well:
function int GetTicketCount() { local int i; local int count; local string s; local BYTE val; count = 0; for (i = 1; i <= 18; i++) { s = "MovieTicket"; if (i < 10) s = s$"0"; s = s$i; GetState(StringToName(s), val); if (val == 1) count = count + 1; } GetState(StringToName("SpentTickets"), val); count = count - val; return count; } function SpendTicket() { local BYTE val; GetState(StringToName("SpentTickets"), val); val = val + 1; SetState(StringToName("SpentTickets"), val); } function UnlockImage(int index) { local string s; s = "ImageUnlocked"; if (index < 10) s = s$"0"; s = s$index; SetState(StringToName(s), BYTE(1)); } function bool ImageUnlocked(int index) { local string s; local BYTE val; s = "ImageUnlocked"; if (index < 10) s = s$"0"; s = s$index; GetState(StringToName(s), val); if (val == 1) return true; return false; }
Level Select and Old Mission Ranks
Technically, the level select page wasn't used, even though there are several ways to use it in the game, abusing bugs or changing some files (check out Spider-Man 2: The Game (Windows, Mac OS X)). Notably, there's a lot of commented-out code. Most notable is that you can see the early mission ranks. 40% of hero points or less in the mission would end it with an "average" rank. 40%-60% of points - "impressive". 60%-80% - "amazing". 80%-90% - "spectacular". And 90-100% - "ultimate".
Only "average" rank was used in the final version and iconic Spidey adjectives from comics were replaced with something more generic... Actually, the game has only 3 ranks: "average", "excellent" and "fantastic".
However, there's a file "WebheadUI.int" in the System folder of the game. It has these lines:
[Mission Select] Locked=Mission Locked Average=Average Impressive=Impressive Amazing=Average Spectacular=Excellent Ultimate=Fantastic
As you can see, the original titles of ranks were overridden with the new ones. However, the first two values still weren't used at all. If you change the second "average" to any other string, then finishing a mission with 0 hero points would end up the mission completed screen displaying this title you just edited.
But anyway, here's the full code of this PageMissionSelect class:
// ==================================================================== // Class: WebHead.PageMissionSelect // Parent: GUI.GUIPage // // ==================================================================== class PageMissionSelect extends WebheadGUIPage dependsOn(GameState); //----------------------------------------------- // Load texture files. //----------------------------------------------- #exec OBJ LOAD FILE=..\textures\GUIContent.utx const NUMLEVELS = 11; const kNumMissionButtons = 30; const L1_Rhino = 1; const L0_Tutorial = 0; const L7_Final = 7; //----------------------------------------------- // Named reference to controls on the page. //----------------------------------------------- var automated GUIButton btnMissionSelectBkgA; var automated GUIButton btnMissionSelectBkgB; var automated GUIButton btnMissionSelectBkgC; var automated GUIButton btnMissionSelectBkgD; var automated GUIButton btnMissionSelectBkgE; var automated GUIButton btnMissionSelectBkgF; var automated GUIMaskButton btnL1A; var automated GUIMaskButton btnL1B; var automated GUIMaskButton btnL1C; var automated GUIMaskButton btnL2A; var automated GUIMaskButton btnL2B; var automated GUIMaskButton btnL2C; var automated GUIMaskButton btnL3A; var automated GUIMaskButton btnL3B; var automated GUIMaskButton btnL3C; var automated GUIMaskButton btnL4A; var automated GUIMaskButton btnL4B; var automated GUIMaskButton btnL4C; var automated GUIMaskButton btnL5A; var automated GUIMaskButton btnL5B; var automated GUIMaskButton btnL5C; var automated GUIMaskButton btnL6A; var automated GUIMaskButton btnL6B; var automated GUIMaskButton btnL6C; var automated GUIMaskButton btnL7A; var automated GUIMaskButton btnL7B; var automated GUIMaskButton btnL7C; var automated GUIMaskButton btnR1A; var automated GUIMaskButton btnR1B; var automated GUIMaskButton btnR1C; var automated GUIMaskButton btnR2A; var automated GUIMaskButton btnR2B; var automated GUIMaskButton btnR2C; var automated GUIMaskButton btnR3A; var automated GUIMaskButton btnR3B; var automated GUIMaskButton btnR3C; var automated GUIMaskButton btnMission[30]; var int CurSelection; var int MissionAvailable[NUMLEVELS]; var automated GUIButton btnCancel; var automated GUIButton btnPlay; var string MissionText[NUMLEVELS]; var string RankText[NUMLEVELS]; var string PointsText[NUMLEVELS]; var float MissionTextTop[NUMLEVELS]; var bool NewPlayer; function bool InGame() { return ((PlayerOwner().Level.Title != "Untitled") && (PlayerOwner().Level.Title != "startup")); } function InitComponent(GUIController MyController, GUIComponent MyOwner) { local int i; Super.InitComponent(MyController, MyOwner); btnMission[0] = btnL1A; btnMission[1] = btnL1B; btnMission[2] = btnL1C; btnMission[3] = btnL2A; btnMission[4] = btnL2B; btnMission[5] = btnL2C; btnMission[6] = btnL3A; btnMission[7] = btnL3B; btnMission[8] = btnL3C; btnMission[9] = btnL4A; btnMission[10] = btnL4B; btnMission[11] = btnL4C; btnMission[12] = btnL5A; btnMission[13] = btnL5B; btnMission[14] = btnL5C; btnMission[15] = btnL6A; btnMission[16] = btnL6B; btnMission[17] = btnL6C; btnMission[18] = btnL7A; btnMission[19] = btnL7B; btnMission[20] = btnL7C; btnMission[21] = btnR1A; btnMission[22] = btnR1B; btnMission[23] = btnR1C; btnMission[24] = btnR2A; btnMission[25] = btnR2B; btnMission[26] = btnR2C; btnMission[27] = btnR3A; btnMission[28] = btnR3B; btnMission[29] = btnR3C; InitMissions(); for (i = 0; i < kNumMissionButtons; i++) { btnMission[i].MenuStateChange(MSAT_Blurry); btnMission[i].bAcceptsInput = bool(MissionAvailable[i/3+1]); } } function SetHints() { btnCancel.Hint = Localize("hints", "Cancel", "WebheadUI"); btnPlay.Hint = Localize("hints", "MissionSelect_Play", "WebheadUI"); } function InitMissions() { local int i; local int myPoints, reqPoints; local GameState gameState; myPoints = 0; NewPlayer = false; for (i = 0; i < NUMLEVELS; i++) MissionAvailable[i] = 0; ForEach PlayerOwner().DynamicActors(class'GameState', gameState) { // if in frontend, use last saved gamestate otherwise use existing //if (!InGame() )) //{ // loadScreen = PageLoadGame( Controller.GetPage(-1) ); // lastGameIndex = loadScreen.Get // lastGameIndex = gameState.GetLastGame(); // // if there is a saved game, load it // if (lastGameIndex >= 0) // { // // load most recently saved game // gameState.LoadLastGame(); // myPoints = gameState.GetTotalHeroPoints(); // } // else // { // // set flag to indicate that player has no saved games, // // in which case we'll disable the mission and Play buttons // NewPlayer = true; // } //} //else myPoints = gameState.GetTotalHeroPoints(); break; } for (i = L1_Rhino; i < NUMLEVELS; i++) { if ( i <= L7_Final ) PointsText[i] = ""; else PointsText[i] = Localize(gamestate.GetLevelPrefix(i), "required points", "maps"); reqPoints = Atoi(PointsText[i]); if ( ( i <= L7_Final && gamestate.GetLevelComplete(i)==1 ) || ( i > L7_Final && myPoints >= reqPoints )) { if (!NewPlayer) MissionAvailable[i] = 1; // load mission name MissionText[i] = Localize(gamestate.GetLevelPrefix(i), "name", "maps"); if ( (i <= L7_Final && gamestate.GetLevelComplete(i)==1) ) RankText[i] = gamestate.GetLevelRank(i); } else { // display "mission locked" instead of mission name MissionText[i] = Localize("mission select", "locked", "webheadui"); } } // disable Play button if new player if (NewPlayer) { btnPlay.bAcceptsInput = false; } } //function string GetLevelRank(int level) //{ // local GameState gameState; // local int heroPts, possiblePts; // local float percentage; // // ForEach PlayerOwner().AllActors(class'GameState', gameState) // { // break; // } // // heroPts = gameState.GetLevelHeroPoints(level); // possiblePts = gameState.GetLevelPossibleHeroPoints(level); // percentage = heroPts / possiblePts; // // // completely placeholder ranking here // if (percentage < possiblePts * 0.4) // return Localize("mission select", "average", "webheadui"); // else if (percentage < possiblePts * 0.6) // return Localize("mission select", "impressive", "webheadui"); // else if (percentage < possiblePts * 0.8) // return Localize("mission select", "amazing", "webheadui"); // else if (percentage < possiblePts * 0.9) // return Localize("mission select", "spectacular", "webheadui"); // else // return Localize("mission select", "ultimate", "webheadui"); //} function bool InternalOnClick(GUIComponent Sender) { local GameState gameState; local string mapName; ForEach PlayerOwner().AllActors(class'GameState', gameState) { break; } if (Sender == btnCancel) { if ( FrontEnd(Controller.MenuStack[0]) != None ) { log("FrontEnd exists"); gameState.PendingMission = ""; gameState.bLoadingMission = false; Controller.CloseMenu(true); } else { PlayerOwner().StopAllMusic(0.0); WebheadGuiController(Controller).MusicHandle = 0; gameState.StartSession(); PlayerOwner().Level.ServerTravel("startup.whr"$"?Game=Webhead.WebheadGameInfo", false); Controller.CloseAll(true); } } else if (Sender == btnPlay) { gameState.PendingMission = gamestate.GetLevelPrefix(CurSelection); if (InGame()) { // show confirmation dialog only if not in MissionMode if ( !gameState.InMissionMode() ) Controller.OpenMenu("WebHead.PageConfirmMission"); else { mapName = Localize(gameState.PendingMission, "map", "maps"); // tell game state we're switching to a temp game state for the specified mission gameState.SetMissionMode(true, gameState.PendingMission); PlayerOwner().StopAllMusic(0.0); WebheadGuiController(Controller).MusicHandle = 0; gameState.bLoadingMission = true; PlayerOwner().Level.ServerTravel(mapName$"?Game=Webhead.WebheadGameInfo", false); Controller.CloseAll(false); } // check mission loading flag; return if true if (gameState.bLoadingMission) return true; } else { // no need for confirmation; load the mission mapName = Localize(gameState.PendingMission, "map", "maps"); // tell game state we're switching to a temp game state for the specified mission gameState.SetMissionMode( true, gameState.PendingMission ); gameState.bLoadingMission = true; log("PageMissionSelect::InternalOnClick"); PlayerOwner().StopAllMusic(0.0); WebheadGuiController(Controller).MusicHandle = 0; PlayerOwner().Level.ServerTravel(mapName$"?Game=Webhead.WebheadGameInfo", false); FrontEnd( Controller.MenuStack[0] ).bMenu = false; Controller.CloseAll(true); return true; } } ProcessMouseClick(Sender); return true; } function InternalOnMousePressed(GUIComponent Sender, bool bRepeat) { ProcessMousePress(Sender); } function InternalOnWatch() { local int i; for (i = 0; i < kNumMissionButtons-2; i+=3) { if (btnMission[i].MenuState == MSAT_Watched) { btnMission[i+1].MenuState = MSAT_Watched; btnMission[i+2].MenuState = MSAT_Watched; } else if (btnMission[i+1].MenuState == MSAT_Watched) { btnMission[i].MenuState = MSAT_Watched; btnMission[i+2].MenuState = MSAT_Watched; } else if (btnMission[i+2].MenuState == MSAT_Watched) { btnMission[i].MenuState = MSAT_Watched; btnMission[i+1].MenuState = MSAT_Watched; } } } function InternalOnDeactivate() { local int i; for (i = 0; i < kNumMissionButtons-2; i+=3) { if (btnMission[i].MenuState == MSAT_Blurry) { btnMission[i+1].MenuState = MSAT_Blurry; btnMission[i+2].MenuState = MSAT_Blurry; btnMission[i+1].bHasFocus = false; btnMission[i+2].bHasFocus = false; } else if (btnMission[i+1].MenuState == MSAT_Blurry) { btnMission[i].MenuState = MSAT_Blurry; btnMission[i+2].MenuState = MSAT_Blurry; btnMission[i].bHasFocus = false; btnMission[i+2].bHasFocus = false; } else if (btnMission[i+2].MenuState == MSAT_Blurry) { btnMission[i].MenuState = MSAT_Blurry; btnMission[i+1].MenuState = MSAT_Blurry; btnMission[i].bHasFocus = false; btnMission[i+1].bHasFocus = false; } } } function ProcessMouseClick(GUIComponent Sender) { local int i; for (i = 0; i < kNumMissionButtons-2; i+=3) { if (Sender == btnMission[i]) { btnMission[i+1].MenuStateChange(MSAT_Focused); btnMission[i+2].MenuStateChange(MSAT_Focused); CurSelection = i/3 + 1; } else if (Sender == btnMission[i+1]) { btnMission[i].MenuStateChange(MSAT_Focused); btnMission[i+2].MenuStateChange(MSAT_Focused); CurSelection = i/3 + 1; } else if (Sender == btnMission[i+2]) { btnMission[i].MenuStateChange(MSAT_Focused); btnMission[i+1].MenuStateChange(MSAT_Focused); CurSelection = i/3 + 1; } else { btnMission[i].MenuState = MSAT_Blurry; btnMission[i+1].MenuState = MSAT_Blurry; btnMission[i+2].MenuState = MSAT_Blurry; btnMission[i].bHasFocus = false; btnMission[i+1].bHasFocus = false; btnMission[i+2].bHasFocus = false; } } } function ProcessMousePress(GUIComponent Sender) { local int i; for (i = 0; i < kNumMissionButtons-2; i+=3) { if (Sender == btnMission[i]) { btnMission[i+1].MenuStateChange(MSAT_Pressed); btnMission[i+2].MenuStateChange(MSAT_Pressed); } else if (Sender == btnMission[i+1]) { btnMission[i].MenuStateChange(MSAT_Pressed); btnMission[i+2].MenuStateChange(MSAT_Pressed); } else if (Sender == btnMission[i+2]) { btnMission[i].MenuStateChange(MSAT_Pressed); btnMission[i+1].MenuStateChange(MSAT_Pressed); } else { btnMission[i].MenuState = MSAT_Blurry; btnMission[i+1].MenuState = MSAT_Blurry; btnMission[i+2].MenuState = MSAT_Blurry; } } } function bool OnDraw(Canvas Canvas) { local bool retval; retval = Super.OnDraw(Canvas); // enable/disable Play button if (CurSelection == -1) btnPlay.bAcceptsInput = false; else if (NewPlayer) btnPlay.bAcceptsInput = false; else btnPlay.bAcceptsInput = true; DrawHeroPoints(Canvas); DrawMissionText(Canvas); DrawRankText(Canvas); DrawPointsRequired(Canvas); return retval; } function SetFont(Canvas C) { if ( C.ClipX <= 640 ) C.Font = SmallFont; else if (C.ClipX < 1024) C.Font = MedFont; else C.Font = BigFont; } function DrawHeroPoints(Canvas C) { local string text; local float XL, YL; local float posX, posY; local float initOrgX, initClipX, initOrgY, initClipY; local GameState gameState; local float centerOn; centerOn = 0.35; ForEach PlayerOwner().AllActors(class'GameState', gameState) { break; } SetFont(C); C.bCenter = false; // draw text C.SetDrawColor(255,255,255,255); // remember these initOrgX = C.OrgX; initClipX = C.ClipX; initOrgY = C.OrgY; initClipY = C.ClipY; // get text metrics text = ""; text = text$gameState.GetTotalHeroPoints(); C.StrLen( text, XL, YL ); C.OrgX = 0; C.OrgY = 0; // draw pts posX = C.SizeX * centerOn - XL/2; // scale offsets posY = C.SizeY * 0.20417; // scale offsets C.SetPos(posX, posY); C.DrawText( text, false ); // reset canvas's Org, Clip values C.OrgX = initOrgX; C.ClipX = initClipX; C.OrgY = initOrgY; C.ClipY = initClipY; } function DrawMissionText(Canvas C) { local int i; local float XL, YL; local float posX; SetFont(C); C.bCenter = false; // draw text C.SetDrawColor(255,255,255,255); C.OrgX = 0; C.OrgY = 0; for (i = L1_Rhino; i < NUMLEVELS; i++) { // get text metrics C.StrLen( MissionText[i], XL, YL ); // right-justify posX = C.SizeX * 0.48 - XL; // center vertically in corresponding button C.SetPos(posX, btnMission[(i-1)*3].ActualTop() + (btnMission[(i-1)*3].ActualHeight() - YL)/2); C.DrawText( MissionText[i], false ); } } function DrawRankText(Canvas C) { local int i; local float XL, YL; local float posX; SetFont(C); C.bCenter = false; // draw text C.SetDrawColor(255,255,255,255); C.OrgX = 0; C.OrgY = 0; for (i = L1_Rhino; i < NUMLEVELS; i++) { // get text metrics C.StrLen( RankText[i], XL, YL ); posX = C.SizeX * 0.5; // center vertically in corresponding button C.SetPos(posX, btnMission[(i-1)*3].ActualTop() + (btnMission[(i-1)*3].ActualHeight() - YL)/2); C.DrawText( RankText[i], false ); } } function DrawPointsRequired(Canvas C) { local float XL, YL; local float posX; local int i; SetFont(C); C.bCenter = false; // draw text C.SetDrawColor(255,255,255,255); C.OrgX = 0; C.OrgY = 0; for (i = L1_Rhino; i < NUMLEVELS; i++) { // get text metrics C.StrLen( PointsText[i], XL, YL ); posX = C.SizeX * 0.6609375; // center vertically in corresponding button C.SetPos(posX, btnMission[(i-1)*3].ActualTop() + (btnMission[(i-1)*3].ActualHeight() - YL)/2);//posY + i*YL); C.DrawText( PointsText[i], false ); } }
Class PageLoadGame has proof, that mission select could be on that screen, but was commented-out:
if ( !btnMissionSelectA.bVisible ) { //btnMissionSelectA.bVisible = true; //btnMissionSelectB.bVisible = true; //btnMissionSelectA.bAcceptsInput = true; //btnMissionSelectB.bAcceptsInput = true; }
Initially, saving was disabled for mission replay, as can be seen in GameState class:
function Save(optional bool afterExitReplay ) { local SpideyPawn spidey; local SpideyPlayerController controller; local string message; //if (!InMissionMode()) //{ // set spidey's health and location variables foreach DynamicActors( class'SpideyPawn', spidey ) { mSpideyHealth = spidey.Health; mSpideyMaxHealth = spidey.MaxHealth; mSpideyLocation.X = spidey.Location.X; mSpideyLocation.Y = spidey.Location.Y; mSpideyLocation.Z = spidey.Location.Z; if ( spidey.bAllowAdrenaline ) mSpideyAdrenaline = 1; if ( spidey.bAllowShoot ) mSpideyWebShooter = 1; break; } // save state if (SaveGameState(afterExitReplay)) { // in game: display progress saved text if ( (InGame() && !InMissionMode()) || (InGame() && !InFreeplay()) ) { ForEach AllActors(class'SpideyPlayerController', controller) { // retrieve message text from objectives.int message = Localize("general", "Progress_Saved", "objectives"); controller.SetProgressMessage( 1, message, class'Canvas'.Static.MakeColor(255,255,255) ); controller.SetProgressTime( 2.0f ); break; } } } else { if (InGame()) { ForEach AllActors(class'SpideyPlayerController', controller) { message = Localize("general", "Save_Failed", "objectives"); controller.SetProgressMessage( 1, message, class'Canvas'.Static.MakeColor(255,255,255) ); controller.SetProgressTime( 2.0f ); break; } } } //} }
By the way, that's how mission rank is actually calculated in the game by GameState class:
// completely placeholder ranking here if (percentage < 0.7) return Localize("mission select", "amazing", "webheadui"); else if (percentage < 0.95) return Localize("mission select", "spectacular", "webheadui"); else return Localize("mission select", "ultimate", "webheadui");
Unimplemented Combat Help Diagrams
PageCombatHelp class has a list of all combat help diagrams in the game:
CombatKey[0] = "CB3_Rhino1"; CombatKey[1] = "CB3_Rhino2"; CombatKey[2] = "WallStBank_DocOck1"; CombatKey[3] = "WallStCitySt_VanWebup"; CombatKey[4] = "ParkAveCitySt_Puma1"; CombatKey[5] = "WallStOscorp_Rhino3"; CombatKey[6] = "WallStOscorp_WebShoot"; CombatKey[7] = "TwistedCity3_Mysterio1"; CombatKey[8] = "TwistedCity3_Mysterio2"; CombatKey[9] = "WharfTrain_TrainRide"; CombatKey[10] = "WharfWarehouse_DocOck2"; CombatKey[11] = "CB3_AdrenalineMeter"; CombatKey[12] = "ParkAveConstruction_Puma2"; CombatKey[13] = "WallStOscorp_Rhino4"; CombatKey[14] = "TwistedCity1_MysterioRobots"; CombatKey[15] = "TwistedCity2_GenTurret";
Some of these diagrams weren't implemented, as can be seen in CombatHelp.int file:
ParkAveConstruction_Puma2_A= ParkAveConstruction_Puma2_B= ParkAveConstruction_Puma2_C= ParkAveConstruction_Puma2_D= ParkAveConstruction_Puma2_E= ParkAveConstruction_Puma2_F= ParkAveConstruction_Puma2_G= ParkAveConstruction_Puma2_H= ParkAveConstruction_Puma2_I= ParkAveConstruction_Puma2_J= ParkAveConstruction_Puma2_K= ParkAveConstruction_Puma2_L= TwistedCity1_MysterioRobots_A= TwistedCity1_MysterioRobots_B= TwistedCity1_MysterioRobots_C= TwistedCity1_MysterioRobots_D= TwistedCity1_MysterioRobots_E= TwistedCity1_MysterioRobots_F= TwistedCity1_MysterioRobots_G= TwistedCity1_MysterioRobots_H= TwistedCity1_MysterioRobots_I= TwistedCity1_MysterioRobots_J= TwistedCity1_MysterioRobots_K= TwistedCity1_MysterioRobots_L=
It means that the devs planned to add tutorial screens for the last Crystal Tower Puma fight and the first Mysterio level. Judging by the names of these diagrams we can only guess what it could mean to show. The first one could show how to use cement against Puma and the second one could be a tutorial on how to fight Mysterio's bots, because they initially were vulnerable only from behind.
Also WallStOscorp_Rhino4 diagram uses the same one as WallStOscorp_Rhino4 used, plus combat key 13 is never used in the game. Looks like they wanted to make a diagram for a coolant room too, but then decided, that the cutscene is enough.
Class GameState shows us which combat help diagrams were made the last. Sadly, only 12, 13 and 14 weren't made at all...
// ACTION_CombatHelp's CombatIndex should be the int value of one of these enum CombatHelpScene { CB3_Rhino1, // 0 CB3_Rhino2, // 1 WallStBank_DocOck1, // 2 WallStCitySt_VanWebup, // 3 ParkAveCitySt_Puma1, // 4 WallStOscorp_Rhino3, // 5 WallStOscorp_WebShoot, // 6 TwistedCity3_Mysterio1, // 7 TwistedCity3_Mysterio2, // 8 WharfTrain_TrainRide, // 9 WharfWarehouse_DocOck2, // 10 // these don't have textures yet... CB3_AdrenalineMeter, // 11 ParkAveConstruction_Puma2, // 12 WallStOscorp_Rhino4, // 13 TwistedCity1_MysterioRobots, // 14 TwistedCity2_GenTurret // 15 };
Mysterio Bots
Judging by this code in MysterioBotPawn class, it should be possible to shoot and attack them only from behind... It's not the case in the game though... However, you can set the variable BehindLimit to 0 using 'editactor class=MysterioBotPawn' command. It's set to 1 by default and that's why the function "IsBehind" is always true. Setting the value to 0 makes vulnerable only their backs, just like it should have been. That's why they have the same yellow things on their backs that the turrets from Twisted City part 2 have. Maybe it's also a reason why the bots turn around very slowly: their turning speed was set keeping in mind that they are only vulnerable from behind. If it really was the case, than having a diagram on the first mysterio level could make more sense, because it's not something all kids could figure out on their own. It's also the reason why they always move their heads like you are hitting them from behind, even if you hit them from the front side. The devs just made their take damage animation knowing that they are vulnerable only from behind!
/** * called by spidey to find the bone + action for his web-up attack. overridden * here to prevent web-up attacks */ function GetBoneTarget( Vector HitLocation, out name bone, out name action ) { // only vulnerable from behind if ( IsBehind(HitLocation) ) { bone = GetFullBoneName('Head'); action = 'shoot'; } } function TakeDamage( int Damage, Pawn instigatedBy, Vector hitLocation, Vector momentum, class<DamageType> damageType) { //TimeStamp("global.TakeDamage() instigator "$instigatedby); // only vulnerable from behind if ( IsBehind(instigatedBy.Location) ) { if ( Health <= Damage ) { PrepareDeath(); Health += Damage; } super.TakeDamage( Damage, instigatedBy, hitLocation, momentum, damageType ); } }
Thug Weapon
Looks like thugs could pick some objects up (clubs, crowbars and grenades). This class is called VillainPickUp_Throwable in the comment, but it's actually ThrowablePickUp.
// VillainPickUp_Throwable // // Thug pickups are handled such that when a thug runs into an item that can be // picked up, the pawn is informed of the PickUp item they've run into and the // associated Inventory item. The pickup is not immediately destoryed because when // the thug is informed of the pickup, they should play a pickup anim that contains // a notify to tell the thug to attach the inventory item to themselves, and to // destory the actual pickup object. // // To change the mesh displayed, edit the static mesh field in the display properties // for ThugPickUp when placed in the level //=============================================================================== // // Spidey can't pickup thug items //
But this class was actually used! It's an invisible object on ParkAve_ConstructionSite map. Puma picks it up and can throw SawBladeProjectile after that!
Old Thug Functionality
Class Thug01Pawn has the code. After the thug was killed, it spawned the same "drunk bubbles" fx you could see when Spidey dies. It was used in the demo, but commented-out for the final release.
function DeathEmitter() { // local vector boneLoc; // boneLoc = GetBoneCoords('Thug01_body:head').Origin; // boneLoc.Z += 20.0f; // DeadEmitter = Spawn( class'FXWebhead.fxDrunkBubbles', self,, boneLoc ); }
Also looks like Fizz Factor really planned to make more animations for different types of damage thug get from Spidey. These cases that go one by one just do the same one thing, when they all were meant to be unique. The same code was also in SuperThugPawn class.
switch ( damageType.Name ) { case 'DamageWebUp': PlayWebBlinded(); break; // // These should be temporary until we get specific anims to play for thugs // when they get hit with a death blow // case 'DamageDeathBlowChin': case 'DamageDeathBlowGut': webhead_damage = class<WebheadDamage>(damageType); if ( webhead_damage != None ) { PlayCombatAnim( webhead_damage.Static.VictimAnim() ); } break; case 'DamageHeadFront': PlayCombatAnim( 'BlockHitHead' ); break; case 'DamageHeadLeft': case 'DamageKickHeadLeft': PlayCombatAnim( 'BlockHitHeadLeft' ); break; case 'DamageHeadRight': case 'DamageKickHeadRight': PlayCombatAnim( 'BlockHitHeadRight' ); break; case 'DamageGut': case 'DamageKickGut': PlayCombatAnim( 'BlockHitGut' ); break; default: PlayCombatAnim( 'BlockHitHead' ); break; }
Also there's this function. It has an early return in the very beginning, so the code after this won't ever be reached. Judging by the comment, it gives thugs an ability to attack you even when their heads are webbed. Actually, they do try to attack you, but that animation doesn't do any damage. Maybe that's the reason.
event NotifyAttack() { local Vector los, x, y, z; return; // SAL - thug kicks too much ass when he's blinded //TimeStamp("NotifyAttack"); if ( Controller.Enemy != None ) { GetAxes( Rotation, x, y, z ); los = Controller.Enemy.Location - Location; if ( ( VSize(los) < CollisionRadius+MeleeRange ) && ( Normal(los) Dot x > 0.5 ) ) { Controller.Enemy.TakeDamage( PunchDamage, self, Controller.Enemy.Location, Vector(Rotation), AttackType.damage ); } } }
SpideyPlayerController
Commented-out exec function in SpideyPlayerController class. Exec functions can be called by player input during the game.
/*exec function KillEnemy() { if (Enemy != None) { VillainPawn(Enemy).Kill(Pawn); } }*/
Also you can see how the devs initially commented the code out, but then decided that it's actually needed, but didn't remove the initial comment:
// this code is commented out because when the following commented out line of code // executes ( PlayerController::PlayerCalcView ), the Pawn is Null // should the Pawn really be Null? // it may be that this method should never make a call to super.PlayerCalcView // it appears that super.PlayerCalcView should be called if spiderman dies if ( spidey != None && spidey.Health <= 0 ) { super.PlayerCalcView(viewActor, cameraLocation, cameraRotation); }
Case 'trip' in function DoAction leads to an empty function, so the action wasn't implemented. Case 'trip' also never happens at all. What was that for? Could Spidey knock enemies off without killing them?
//-------------------------------------------------------------------------------------------------- // function TripTarget() //-------------------------------------------------------------------------------------------------- function TripTarget() { }
Another commented-out exec function to stop adrenaline whenever you want:
//exec function EndAdrenaline() //{ // if( !Spidey.IsOnWall() ) // Spidey.StopAdrenalineTiming(); //}
This comment about how blocking (evasion) worked wasn't changed after they changed the script later. Here it describes the same behaviour as was seen in the demo.
//----------------------------------------------------------------------------- // PlayerBlocking state // // Blocking is currently hooked up the the space bar. The block state can only be // entered from the PlayerWalking state and then only if spidey's physics state is // walking and he is not currently on a wall. // // If the above holds true, then the block state is entered when the shift key // is pressed, and exited when it is released. //-----------------------------------------------------------------------------
Here some "CombatShield" state is commented-out:
function bool CanBlock() { return ( ( Level.TimeSeconds > Spidey.NextBlockTime ) && // ( Spidey.GetStateName() != 'CombatShield' ) && ( Spidey.Physics == PHYS_Walking ) && ( BlockingEnabled && !Spidey.IsOnWall() ) ); }
Another commented-out exec function, now to enable adrenaline at any moment:
//---------------------------------------------------------------------------------------------- // exec function Adrenaline() //---------------------------------------------------------------------------------------------- // exec function Adrenaline() // { // if( !Spidey.IsOnWall() ) // Spidey.StartAdrenalineTiming(); // }
A code for unused danger sense block action:
//---------------------------------------------------------------------------------------------- // exec function DangerSenseBlock() //---------------------------------------------------------------------------------------------- exec function DangerSenseBlock() { if (DangerSenseAction.Reactions[DangerSenseReactionIndex].ReactionType == DANGER_Block) { NextReaction(); } else { Failed(); } }
SpideyPawn
Tons of commented-out exec functions in the SpideyPawn class. They don't look really useful, though...
// exec functions: /*exec function Glow() { bAdrenalineModeActive = !bAdrenalineModeActive; if ( bAdrenalineModeActive ) { StartAdrenalineEffects(); } else { StopAdrenalineEffects(); } } exec function SetCollisionHeight(float height) { NativeSetCollisionHeight(height); } exec function SetMeshOriginX(float x) { NativeSetMeshOrigin(x*Vect(1.0f, 0.0f, 0.0f), Vect(1.0f, 0.0f, 0.0f)); } exec function SetMeshOriginY(float y) { NativeSetMeshOrigin(y*Vect(0.0f, 1.0f, 0.0f), Vect(0.0f, 1.0f, 0.0f)); } exec function SetMeshOriginZ(float z) { NativeSetMeshOrigin(z*Vect(0.0f, 0.0f, 1.0f), Vect(0.0f, 0.0f, 1.0f)); } exec function RefPose() { NativeRefPose(); } exec function AnimMixer() { NativeClearAllAnims(); bAnimMixer = !bAnimMixer; if (!bAnimMixer) { bInitializeAnimation = false; } } exec function ClearAllAnims() { if (bAnimMixer) { NativeClearAllAnims(); } } exec function SetAnim(int channel, name animName, optional float tweenTime) { if (bAnimMixer) { NativeSetAnim(channel, animName, tweenTime, true); } } exec function ClearAnim(int channel) { if (bAnimMixer) { NativeClearAnim(channel); } } exec function SetAnimBlend(int channel, float alpha) { if (bAnimMixer) { NativeSetAnimBlend(channel, alpha); } } exec function SetMaxSwingDist(float distance) { MaxSwingDistance = distance; } exec function SetMinSwingDist(float distance) { MinSwingDistance = distance; } exec function SetWebSpeed(float speed) { WebAirSpeed = speed; }*/ /*exec function SetLightDist(float d) { ShadowProjector(Shadow).LightDistance = d; ShadowProjector(Shadow).ShadowTexture.LightDistance = d; ShadowProjector(Shadow).UpdateShadow(); } exec function SetLightTrace(float d) { ShadowProjector(Shadow).MaxTraceDistance = d; ShadowProjector(Shadow).UpdateShadow(); }*/
Some interesting commented-out string in ReleaseWebRope function. It gets destroyed instead of being reserved. Does it mean that web ropes could live a bit longer, just like dead thugs?
function ReleaseWebRope() { if (WebRope != None) { //WebRope.Reserve(); WebRope.Destroy(); } WebRope = None; }
There's this fall damage function in the class, but Spidey never gets fall damage. There was a weird fall damage in KnowWonder's Harry Potter and the Sorcerer's Stone (Windows, Mac OS Classic, Mac OS X), but you could get at least a small fraction of damage by falling using ghost mode. It isn't the case in Spider-Man 2 - no height on any map is enough to get fall damage.
function TakeFallingDamage() { LastFallVelocity = Velocity; super.TakeFallingDamage(); }
Also it's not an unused code, but the game never tells you about it and it's not likely you noticed this, so the more you know!
// // spidey takes half damage when in adrenaline mode // if( bAdrenalineModeActive ) { Damage /= 2.0f; }
Spidey's grunts and animations after getting the damage from falling objects were specifically disabled for the train battle and it's not a mistake:
case 'DamageTrainBattle': // these get played in takedamage because for some reason when they occur // here there is a delay between the sounds and the spidey fangs showing up // Grunt = PainGrunts[ Rand( PainGrunts.Length ) ]; // HitSound = ElecticShock; showEffect = false; break;
These functions could be callable vie the console, but the "exec" was commented-out:
/*exec*/ function UpgradeAdrenaline(bool giveAdrenaline) { bAllowAdrenaline = true; if ( giveAdrenaline ) { IncrementAdrenaline( AdrenalineMax ); } } /*exec*/ function UpgradeWebShoot() { bAllowShoot = true; }
Web Meter
Function GameStat of PageStatus still has the code for web meter data:
case SS_WebFluid: result = spidey.WebMeter; break; }
However, the actual code drawing it was commented-out:
// draw WEB FLUID //text = ""; //text = text$GetStat(SS_WebFluid); //posY = posY + 0.7 * YL; // scale offsets //C.SetPos(posX, posY); //C.DrawText( text, false );
Puma
This was commented-out in state CombatShield of PumaPawn:
// function TakeDamage( int Damage, Pawn EventInstigator, vector HitLocation, vector Momentum, class<DamageType> damageType ) // { // //TimeStamp( "TakeDamage" ); // GotoState( , 'begin' ); // }
Looks like another workaround...
// // don't allow puma to take area damage because it can cause some wonky stuff // to happen if he's been webbed up // if( damageType.name == 'DamageArea' ) { return; }
More interesting comments of TakeDamage function of PumaPawn:
// // Just pass the webup damage on because puma doesn't dodge this // // // if puma can block the attack, then check and see if he should // go to the combat shield state // // // This is because it might be possible for puma to get hit and // and be in the CounterAttack state, so this should prevent him // from firing back so quickly after getting hit // // not really sure if this is necessary, but it seemed to help // need to spend more time looking at this to see if it is or not. // whicked giant hack because even though we're telling him to exit // the combat shield state, when super.TakeDamage is called it's still // calling the combat shield version which reduces the damage by ShieldDamageFactor // this way, he'll still take the right amount of damage.
Another not done todo,now in MeleeAttack state of PumaPawn:
// TODO: Implement this properly Pawn.Fire(); FinishAnim( 0 ); FinishAttack();
Commented-out code in PumaController class:
//----------------------------------------------------------------------------- // Function: NotifyHealthEvent // // Description: //----------------------------------------------------------------------------- function NotifyHealthEvent() { GotoState( 'HealthEventTriggered' ); // Puma.ReleaseBaseAnim( ); // make sure puma release the anim channel before the event // TriggerEvent( PendingEvent, Puma, None ); // PendingEvent = ''; //TimeStamp( "NotifyHealthEvent()" ); }
Commented-out code in Think() function of PumaController:
// if ( PendingEvent != '' ) // { // TriggerEvent( PendingEvent, Puma, None ); // PendingEvent = ''; // }
This state is unused, but you can enable it if you use command "editactor class=pumapawn" and change bCanBeWebbed in Webhead tab to True.
/////////////////////////////////////////////////////////////////////////////// // state: WebBlocked // // Description: Handles playing of reaction anim. Unless puma is preparing to // throw an item, he blocks spidey's web attacks /////////////////////////////////////////////////////////////////////////////// state WebBlocked { ignores SeePlayer, EnemyNotVisible; function BeginState() { //TimeStamp( "BeginState()" ); Pawn.Acceleration = vect(0,0,0); Destination = Pawn.Location; Focus = Enemy; } function EndState( ) { //TimeStamp( "EndState()" ); Puma.IsWebBlinded = false; } begin: RandomSound( VOTakeHit ); Puma.ForcePlayBaseAnimOnce('WebBlock', 1.0, 0.1f); FinishAnim( 0 ); Think(); }
Well...
// // The ground lunge retreat doesn't work so well, especially on roof tops and stuff // where he might end up falling off a building, so I've removed it. //
Hm, looks like this todo was done, but it wasn't removed... It's not the only one like this.
// // TODO: Play lunge attack anim // Puma.ForcePlayBaseAnimOnce( 'GroundLungeForwardsAttack', 1.0f, 0.2f );
Looks like they initially planned to disable Spidey's moving ability when Puma started his combo:
//TimeStamp( "BeginState()" ); // SpideyPlayerController( Enemy.Controller ).GotoState( 'PlayerRooted' ); Puma.AttackType = Puma.ComboAttack; Puma.UseCrouchIdle = false; Puma.Charging = false; StunnedTime = 0.0f;
Another interesting workaround:
// // Set this to true so that the physicsRotation code doesn't get // executed once he jumps so that he doesn't rotate in the air // to face spidey. // Puma.bInterpolating = true;
Doc Ock
Commented-out code and unimplemented functionality in DocOcTrainController class. Notably, you could web blind Ock. Also function Destroyed seem to be completely unused. For some reason train controller also has functionality of the final showdown Ock controller, but it's obviously unused...
event EnemyNotVisible() { CanSeeSpidey = false; //WanderOrCamp(); } //----------------------------------------------------------------------------- // Function: Startle // // Description: This function is called from the AvoidMarker class which is a type of trigger. Startle // is not implemented in AIController but the idea of a startle function is useful. Consider // implementing this function and maybe calling it more. //----------------------------------------------------------------------------- event Startle( Actor theStartler ) { } //----------------------------------------------------------------------------- // Function: NotifyBump // // Description: Probably remove this method //----------------------------------------------------------------------------- event bool NotifyBump( Actor Other ) { // assume this is a check for spiderman /* if ( Other == GetMyPlayer() && DocOc.LastBumpVictim == None ) { Enemy = Pawn(Other); DocOc.LastBumpVictim = Enemy; GenerateSpideyMomentum( Enemy ); log("global NotifyBump generates hit"); log(" inside state " $GetStateName() ); log(" enemy state " $Enemy.Controller.GetStateName() ); GotoState( 'SpideyBump' ); } */ return true; } function NotifyTakeHit(pawn InstigatedBy, vector HitLocation, int Damage, class<DamageType> damageType, vector Momentum) { log("NotifyTakeHit "$InstigatedBy); super.NotifyTakeHit(InstigatedBy, HitLocation, Damage, damageType, Momentum); //if ( InstigatedBy == Enemy ) // RandomSound(VOTakeHit); if ( damageType.Name == 'DamageWebUp' ) { //IsWebBlinded = true; PlayWebBlock(); } } /----------------------------------------------------------------------------- // Function: Destroyed // // Description: //----------------------------------------------------------------------------- simulated function Destroyed() { if ( DeadEmitter != None ) DeadEmitter.Destroy(); if ( LeftArm != None ) LeftArm.Destroy(); if ( LeftFoot != None ) LeftFoot.Destroy(); if ( RightArm != None ) RightArm.Destroy(); if ( RightFoot != None ) RightFoot.Destroy(); GotoState('Dead'); super.Destroyed(); } state PreStunned { function PreStunFinished() { bPreStunFinished = true; } event tick( float deltaTime ) { if ( LeftFoot.GetStateName() != 'PreStunned' ) LeftFoot.GotoState('PreStunned'); if ( RightFoot.GetStateName() != 'PreStunned' ) RightFoot.GotoState('PreStunned'); if ( LeftArm.GetStateName() != 'PreStunned' ) LeftArm.GotoState('PreStunned'); if ( RightArm.GetStateName() != 'PreStunned' ) RightArm.GotoState('PreStunned'); if ( LeftFoot.GetStateName() == 'PreStunned' && RightFoot.GetStateName() == 'PreStunned' && LeftArm.GetStateName() == 'PreStunned' && RightArm.GetStateName() == 'PreStunned' && DocOc.GetAnimSequence(0) != 'Explosion_BD' ) { //log("play explosion"); DocOc.ForceLoopBaseAnim('Explosion_BD', 1.0f, 0.2f ); } if ( LeftFoot.GetStateName() == 'PreStunned' && RightFoot.GetStateName() == 'PreStunned' && LeftArm.GetStateName() == 'PreStunned' && RightArm.GetStateName() == 'PreStunned' && bPreStunFinished == true ) { bPreStunFinished = false; DeathEmitter(); //log("DocOcShowDownController::PreStun PreStunFinished"); MacroGotoState('Stunned'); } } event bool NotifyBump( Actor Other ) { return true; } begin: tmpDirection = DocOc.Location + (10.0 * vector(DocOc.Rotation)); MoveTo(tmpDirection, None ); } state Stunned { function Timer() { log("stun finished going to PostStunned"); if ( LeftFoot.GetStateName() == 'Stunned' && LeftArm.GetStateName() == 'Stunned' && RightFoot.GetStateName() == 'Stunned' && RightArm.GetStateName() == 'Stunned' ) { PlayReviveVO(); if ( DeadEmitter != None && DeadEmitter.bHidden == false ) { DeadEmitter.bHidden = true; } bStunnedFinished = false; MacroGotoState('Waiting'); //MacroGotoState('PostStunned'); } else { //log("can't switch an arm to state stunned"); SetTimer(1.0f, false); } } function NotifyTakeHit(pawn InstigatedBy, vector HitLocation, int Damage, class<DamageType> damageType, vector Momentum) { super.NotifyTakeHit(InstigatedBy, HitLocation, Damage, damageType, Momentum); runningDamage += Damage; if ( runningDamage > DocOc.sdMaxStunnedDamage ) SetTimer(0.0001f, false); } event tick( float deltaTime ) { if ( LeftFoot.GetStateName() != 'Stunned' ) LeftFoot.GotoState('Stunned'); if ( RightFoot.GetStateName() != 'Stunned' ) RightFoot.GotoState('Stunned'); if ( LeftArm.GetStateName() != 'Stunned' ) LeftArm.GotoState('Stunned'); if ( RightArm.GetStateName() != 'Stunned' ) RightArm.GotoState('Stunned'); } event bool NotifyBump( Actor Other ) { return true; } begin: PlayStun(); MoveTo(tmpDirection, None ); runningDamage = 0; //if ( DocOc.Health < DocOc.sdLowerHealth ) // SetTimer(DocOc.sdStunLengthLower, false); //else SetTimer(DocOc.sdStunLength, false); } //currently not used state PrePostStunned { function Trigger(Actor Other, Pawn Instigator) { MacroGotoState('PostStunned'); } begin: PlayStun(); MoveTo(tmpDirection, None ); } state PostStunned { function PostStunFinished() { log("DocOcShowDownController::PostStun PostStunFinished"); bStunnedFinished = false; MacroGotoState('Waiting'); } event tick( float deltaTime ) { } function NotifyTakeHit(pawn InstigatedBy, vector HitLocation, int Damage, class<DamageType> damageType, vector Momentum) { } event bool NotifyBump( Actor Other ) { return true; } begin: DocOc.ForceLoopBaseAnim('Recover_BD', 1.0f, 0.2f ); MoveTo(tmpDirection, None ); } function WebBlockFinished() { } function PreStunFinished() { } function PostStunFinished() { } function TurnAroundFinished() { } function MaybeAddLeft(Actor other ) { } function MaybeAddRight(Actor other ) { } function DeathEmitter() { local vector boneLoc; local vector rot; log("DeathEmitter function called "); boneLoc = GetBoneCoords('DocOc_body:head').Origin; rot = Vector(DocOc.Rotation); boneLoc.X += -18.0f * rot.X; boneLoc.Y += -18.0f * rot.Y; boneLoc.Z += 50.0f; if( DeadEmitter == None ) DeadEmitter = Spawn( class'FXWebhead.fxDrunkBubbles', self,, DocOc.Location + boneLoc ); else { DeadEmitter.bHidden = false; DeadEmitter.SetLocation( DocOc.Location + boneLoc ); } if ( DeadEmitter != None) log("DeadEmitter spawned at "$boneLoc); } function PlayReviveVO() { RandomTalk(VORevived); quipTime = Level.TimeSeconds + 10.0; }
Commented-out code in DocOcPawn class. He indeed has web block and even some functionality for it, but it does nothing in the game... Most likely because unlike Puma he doesn't have NotifyWebAttack function.
//if ( PlayHitAnim == true ) //{ // if ( degreeRadiansFrontBack < 0.0 ) // PlayCombatAnim( HitHeadFrontName ); // else // PlayCombatAnim( HitHeadBackName ); //} function NotifySwipeMediumFinished() { log("DocOcPawn::NotifySwipeMediumFinished"); if ( Controller != None ) { if ( DocOcTrainController( Controller ) != None ) DocOcTrainController( Controller ).SwipeMediumFinished(); //else if ( DocOcShowDownController( Controller ) != None ) // DocOcShowDownController( Controller ).SwipeMediumFinished(); } } //----------------------------------------------------------------------------- // Function: EnemyNotVisible // // Description: This event is called whenever the Enemy is not visible. Like SeePlayer this will be // called very frequently unless you switch to a state that ignores EnemyNotVisible or // call Disable(‘EnemyNotVisible’). //----------------------------------------------------------------------------- event EnemyNotVisible() { CanSeeSpidey = false; //WanderOrCamp(); } //----------------------------------------------------------------------------- // Function: Startle // // Description: This function is called from the AvoidMarker class which is a type of trigger. Startle // is not implemented in AIController but the idea of a startle function is useful. Consider // implementing this function and maybe calling it more. //----------------------------------------------------------------------------- event Startle( Actor theStartler ) { } function NotifyTakeHit(pawn InstigatedBy, vector HitLocation, int Damage, class<DamageType> damageType, vector Momentum) { //super.NotifyTakeHit(InstigatedBy, HitLocation, Damage, damageType, Momentum); //if ( InstigatedBy == Enemy ) // RandomSound(VOTakeHit); if ( damageType.Name == 'DamageWebUp' ) { //IsWebBlinded = true; PlayWebBlock(); } } event Tick(float deltaTime ) { //if ( bStrikeFinished == true && Enemy.bCollideActors == true ) if ( bStrikeFinished == true ) { PlayIdle(); log("state strikePart2 going to camping"); GotoState('Camping'); } super.Tick( deltaTime ); } // null functions that aren't needed //event bool NotifyBump( Actor Other ) //{ // just return true because Spiderman needs to be able to hit DocOc // return true; //} //function IdleFinished() //{ // PlaySwipe(); // log("MeleeAttack IdleFinished()called"); //} if ( bFirstTimeInVault ) { log("first time in vault"); bFirstTimeInVault = false; triggerEvent('DocVaultDoor', self, None ); //triggerEvent('SpideyInDocVault', self, None ); } else { log("vaultDoorClosed::OpenVaultDoor "); triggerEvent('LaserTurretOn', self, None ); triggerEvent('DocVaultDoor', self, None ); //triggerEvent( 'ElectricFloorActive', self, None ); //StopSound(DocOc, electricFloorSound); } function PlayWebBlock() { if ( DocOc.GetAnimSequence( 0 ) == 'PullFree_BD' || DocOc.GetAnimSequence( 0 ) == 'Strike_BD' || DocOc.GetAnimSequence( 0 ) == 'StuckInFloor_BD' ) return; if ( DocOc.GetAnimSequence( 19 ) == 'FlipsSwitch_MA' ) { DocOc.PlayAnim('WebBlock_ML', 1.0f, 0.1f, 20); DocOc.AnimBlendParams(20, 1.0, 0.0, 0.0, 'DocOc_Body:UpperMechArms_RootPa', true ); } else { DocOc.ForcePlayBaseAnim('WebBlock_BD', 1.0f, 0.1f ); DocOc.PlayAnim('WebBlock_MA', 1.0f, 0.1f, 19); DocOc.AnimBlendParams(19, 1.0, 0.0, 0.0, 'DocOc_Body:UpperMechArms_RootPa', true); } }
Subtitles
An unused class for subtitles:
class ACTION_Say extends LatentScriptedAction; // Play a dialog sound file accompanied by subtitles. When the sound file is finished, the action is complete. var() string dlgName; var() string dlgPackage; var() bool dlgSubtitle; var string dlgPath; var PlayerController PC; function bool InitActionFor(ScriptedController C) { dlgPath = dlgPackage$"."$dlgName; ActionString = "ACTION_Say"@dlgPath; ForEach C.DynamicActors(class'PlayerController', PC) { break; } if (PC != none) { if (PC.PlaySoundObject(dlgPath, SLOT_Talk, 1, false, 100000, , false)) { if (dlgSubtitle) { PC.myHUD.DisplaySubtitle(dlgName, true); } } else { log("ERROR: Action_Say failed to load sound "$dlgPath); } } C.CurrentAction = self; return true; } function bool StillTicking(ScriptedController C, float DeltaTime) { if (PC != none) { if (PC.SoundPlaying(dlgPath)) { return true; } else { PC.myHUD.DisplaySubtitle("", false); PC = None; } } return false; } function bool TickedAction() { return true; } function bool CompleteWhenDoneTicking() { return true; } function string GetActionString() { return ActionString; }
Misc
Some weird unused class. Summoning it spawns an invisible object. Most of the code is commented-out. Judging by one of the comments, these coins could be collected and then used if you are at full health and just took damage. It sounds similar to those thieves from Shrek 2 (Windows), they took your coins instead of health if you had any. And Fizz Factor was subsidiary of Amaze Entertainment as well as KnowWonder, who developed Shrek 2 (Windows). On the same Unreal Engine, by the way. So Fizz Factor really might have been take this function from them. Why? Who knows... There's a lot of classes starting with KW, which is most likely KnowWonder.
//=============================================================================== // [SpiderRedCollection] //=============================================================================== class SpiderRedCollection extends KWInventoryCollection notplaceable; const NUM_COINS_FOR_HEALTH = 100; function PickupFunction(Pawn Other) { // local int maxhealth; local SpideyPawn playerHero; // local Actor A; foreach DynamicActors( class'SpideyPawn', playerHero ) ; Super.PickupFunction(Other); //in case at full health, and have many coins, then take damage, coins should be used //but only if will result in a full powerup...level design has assured this is not needed /* if(playerHero.GetInventoryCount('Coin') >= NUM_COINS_FOR_HEALTH) //actually ==, error catching { if(playerHero.health < 1000 && playerHero.TotalHealthIcons < playerHero.MAX_NUM_LIVES) { playerHero.SubtractFromInventoryCollection(class 'CoinCollection', NUM_COINS_FOR_HEALTH); playerHero.TotalHealthIcons++; //another health icon maxhealth = 100 * playerHero.TotalHealthIcons; //max out health playerHero.health = maxhealth; if(playerHero.health > 1000) playerHero.health = 1000; } } */ }
This function of the ShowDownThrowableProjectile class is commented-out:
function HitWall (vector HitNormal, actor Wall) { //log("ShowDownThrowableProjectile::hitwall::self is " $self$" velocity " $velocity ); //if( Velocity.X != 0.0 ) // Super.HitWall( HitNormal, Wall ); //if ( bCollideWorld && bCollideActors ) // Super.HitWall( HitNormal, Wall ); }
There's also one very emotional dev comment:
// what a freakin mess // for some reason, somewhere, the velocity and acceleration // do not do what Deflect() does, so gave up trying to find // the spot in UNREAL that is killing me
Most likely that's why the barrels deflected with web projectiles behave a bit weird... They can stuck near the moving platforms and also they don't get destroyed - the barrels just float up in the sky.
Some commented-out code in Destroyed() function of RoidProjectile class:
//if ( DestroyedType != None ) // Spawn(DestroyedType);
This function of RedSpider class is fully commented-out. It's these coins from the 50-coin mini-game in Bronx.
//function Timer() //{ // local Vector Direction; // // if ( !bReachedDest ) // { // Direction = Normal( MoveToward.Location - Location ); // SetLocation( Location + Direction * MoveAccel); // // if ( VSize( MoveToward.Location - Location ) < MoveAccel ) // bReachedDest = true; // } // else // { // // maybe do something (rotate?) // SetTimer( 1.0f, true ); // } //}
This SpiderGoldCollectionClass is unused, but it was initially used to give you adrenaline. It's also called SpiderRedCollection in the comment... The actual adrenaline object class is SpiderGold.
//=============================================================================== // [SpiderRedCollection] //=============================================================================== class SpiderGoldCollection extends KWInventoryCollection notplaceable; function PickupFunction(Pawn Other) { Super.PickupFunction(Other); //WebheadGameInfo(Level.Game).Player.AddAdrenaline( WebheadGameInfo(Level.Game).Player.AdrenalineMax ); WebheadGameInfo(Level.Game).Player.AdrenalinePowerup(); }
A funny comment in the KWPickUp class:
// Adjust initial rotation phase to avoid "Steamboat Willie" syndrome
They planned to create a special animation for a death blow to gut in DamageDeathBlowGut class, but left the basic "KnockedOffFeet" one. Look like they didn't get a good one...
static function name VictimAnim() { return 'KnockedOffFeet'; // use this for now, until we get a good death blow gut anim }
Some code is also commented-out in InitActionFor function of that class:
/*if (MovieIndex == MovieList.Length) { //open level //Player.Level.ServerTravel(NextLevel, false); return; } */
In WebUpTarget class we can see some TODO that wasn't done. It's the class of those webs on the heads of webbed up thugs and Puma.
// todo: apply thicker texture or increase drawscale Skins[0]=Texture'CombatTextures.HeadWeb.HeadWeb03';
Another todo is in the WebSwingPoint class:
// TODO: Is there some way to get around using texture variables here?
Another "temporary" fix comment is in the WebheadPawn class:
// This might not be the best fix, but for now is should work. // It seems that the mesh might not necessarily be constructed when // this function is called, but HasAnim relies on the mesh being there // so check to see if the mesh is none before calling has anim
There's also some named comment:
// force shadows off if in 16-bit mode. --ryan.
More commented-out code of the class:
/* if (ShadowType == SH_Rendered) log("*** using rendered shadows ***"); else if (ShadowType == SH_Blob) log("*** using blob shadows ***"); else log("*** not using shadows ***"); */ // SAL - commented out //ForEach AllActors(class'Sunlight', sun) break; //if (sun != None) //{ // ShadowProjector(Shadow).LightDirection = -Vector(sun.Rotation); //} //else //{ // ShadowProjector(Shadow).LightDirection = Normal(vect(1,1,3)); //}
InternalListDeActivate and InternalListClick functions of WebheadGUIComboBox class some commented-out code too:
//if (!Edit.bPendingFocus) // MyListBox.Hide(); //List.InternalOnClick(Sender); //Edit.SetFocus(none); //return true;
This code is commented-out in several functions of VillainControllerclass:
//if ( VSize(goal) > run_radius ) // return false; // don't run *too* far
But there's more:
/*if ( pointReachable(goal) ) { Destination = goal; return true; }*/
The insides of this function of VanController class is commented-out:
function SetCombatTimer() { //SetTimer(1.2 - 0.09 * FMin(10, Skill), True); }
And there's some commented-out strings in FindBestPathToward function:
if ( (A == Enemy) && (A != None) ) { //FailedHuntTime = Level.TimeSeconds; //FailedHuntEnemy = Enemy; }
Even more here:
function bool FindPathAwayFrom(Actor Other) { //local Vector goalPos; //local float run_radius; local PathNode goalNode; local float goalDist; //run_radius = 800; // determined by distance between pathnodes //goalPos = (Pawn.Location - Other.Location); //goalPos = Normal(goalPos) * run_radius; //goalPos = goalPos + Pawn.Location; Foreach Pawn.RadiusActors( class'PathNode', goalNode, 1200 ) { // find all nearby pathnodes // choose one furthest from player // todo: choose from remaining randomly if ( VSize(goalNode.Location - Other.Location) > goalDist ) { goalDist = VSize(goalNode.Location - Other.Location); MoveTarget = goalNode; } } MoveTarget = FindPathToward( MoveTarget ); Focus = MoveTarget; return true; }
Some todos in the Tripwire class. It's those red security lasers.
if ( Thing1 == None ) // deprecated. use edfindable! if ( Thing2 == None ) // deprecated. use edfindable! Beam.SetBase(Thing1); // TODO: figure out why this has no effect
These functions of ThugController class is almost 100% commented-out. It always returns false now, when it was much more complicated at first:
function bool NotifyHitWall(vector HitNormal, actor Wall) { //TimeStamp("global.NotifyHitWall()"); //return AdjustAround(Wall); //GotoState( 'TacticalMove' ); return false; /* if (Pawn.Physics == PHYS_Falling) return false; if ( Enemy == None ) { WhatToDoNext(18); return false; } if ( bChangeDir || (FRand() < 0.5) || (((Enemy.Location - Pawn.Location) Dot HitNormal) < 0) ) { Focus = Enemy; WhatToDoNext(19); } else { bChangeDir = true; Destination = Pawn.Location - HitNormal * FRand() * 500; } return true; */ } function NotifyTakeHit(pawn InstigatedBy, vector HitLocation, int Damage, class<DamageType> damageType, vector Momentum) { //if ( !FindPathAwayFrom(Enemy) ) //{ // super.NotifyTakeHit(InstigatedBy, HitLocation, Damage, damageType, Momentum); // if ( damageType.Name != 'DamageWebUp' ) // Think(); //} }
Some code was also commented-out in PickDestination function of the class:
//if ( bStrafeDir ) // transverse_part *= -1; //bStrafeDir = !bStrafeDir; //bForcedDirection = true; //StartTacticalTime = Level.TimeSeconds;
And one more todo:
Pawn.Acceleration = vect(0,0,0); // TODO: figure out why this is necessary
Commented-out function in SuperThugController:
function ReleaseSpidey() { // Thug.bCanBeWebbed = true; // Thug.bCanBeAttacked = true; // SpideyPlayerController( Enemy.Controller ).GotoState( 'PlayerWalking' ); }
In SpideyHud class this function could be executable (callable via console):
/*exec*/ function SetHealth( int val ) { local SpideyPawn spidey; spidey = WebheadGameInfo(Level.Game).Player; spidey.Health = val; }
Some todo in SecurityCamera class:
if ( Spotlight != None ) { // TODO: figure out why i have to explicitly set this! Spotlight.SetRotation( Barrel.Rotation ); }
RoidSpawner and MysterioPawn classes have comments with links to the documentation of the game. Sadly, it's localhost, so we can't access it in any way. But now we know, that at least one of the devs was a Tolkien fan.
// http://valinor:8080/FizzWiki/RoidSpawner // http://valinor:8080/FizzWiki/MysterioAI
This was in RoidSpawner class. Might be a mistake, since this comment is in PidgeonGroup class as well...
//-------------------------------------------------------------------------------------------------- // Construct an array of circular cells having a radius equal to PidgeonRadius around the group's // location. Randomly choose a cell for each pidgeon. Once a cell is chosen for a pidegon, place // the pidgeon at a random position within a circle centered on the cell and having a radius equal // to PidgeonOffset. // // ----- // / \ // ----- 7 ----- // / \ / \ // ----- 18 ----- 8 ----- // / \ / \ / \ // . 17 ----- 1 ----- 9 . // \ / \ / \ / // ----- 6 ----- 2 ----- // / \ / \ / \ // . 16 ----- 0 ----- 10 . // \ / \ / \ / // ----- 5 ----- 3 ----- // / \ / \ / \ // . 15 ----- 4 ----- 11 . // \ / \ / \ / // ----- 14 ----- 12 ----- // \ / \ / // ----- 13 ----- // \ / // ----- // // The center cell is in ring 0. // Rings greater than 0 have 6*ring cells in them. // The total number of cells is equal to 3*rings^2 + 3*rings + 1. // The number of rings needed for n pidgeons is equal to CEIL((SQRT(12*n - 3) - 3)/6). //--------------------------------------------------------------------------------------------------
But there's more comments in RoidSpawner class:
// SAL - programmers know best //SpawnRadius = 30; //SpawnOffset = 5; //SpawnHeight = 800;
Some unemplemented functions in RhinoPawn class:
/** * This function is called from the AvoidMarker class which is a type of trigger. Startle * is not implemented in AIController but the idea of a startle function is useful. Consider * implementing this function and maybe calling it more. */ event Startle( Actor theStartler ) { } /** * todo: look for alarm triggers, or failing that, simply run away */ state Fallback { }
Commented-out code in Think() function:
if ( CanSeeSpidey ) { if ( CheckIfNearPlayer(Pawn.MeleeRange) ) GotoState('MeleeAttack'); //else if ( Pawn.Health < FEAR_THRESHOLD ) // GotoState('Fallback'); //else if ( Level.TimeSeconds > NextChargeTime ) // GotoState('Charging'); else if ( !MaybePatrol() ) MaybeCharge(); }
More unused code of RhinoPawn:
// stop if target has moved out of reach //if ( dist < RHINO_MAXPATHDIST && !ActorReachable(Enemy) ) //{ // // stop charging; there's something in the way! // Destination = Pawn.Location; //}
This comment in PedestrianPawn reveals some important truth about the world of the game:
// Pedestrians are immortal.
Commented-out code in PatternGunTurret class:
if ( TimeAlongPath > TimeToTracePath ) { //mDir = BACKWARD; //TimeAlongPath = TimeToTracePath; //curIndex = PathDist.Length; TimeAlongPath *= -1.0; }
Commented-out function in PageVideo class:
//----------------------------------------------------------------------------- // Delegate to refresh event //----------------------------------------------------------------------------- function Refresh() { /* PageVideo_OnLoadINI(cmbResolution,""); PageVideo_OnLoadINI(cmbColorDepth,""); PageVideo_OnLoadINI(cmbPreset,""); */ }
Todo list in PageStatus class. Most likely it's Marshall Kunze - one of the programmers, but could also be a game designer Marshall Andrews, who knows...
/* MARSHALL'S TO DO LIST: - put configurable variables (positioning, etc.) in .ini file - figure out how to fit text in given space on low resolutions - encapsulate text drawing better so it's not just cut-and-paste code */
Another Marshall's todo list in PageControls class:
/* MARSHALL'S TO DO LIST: - clean this file up - put configurable variables (positioning, etc.) in .ini file - encapsulate text drawing better so it's not just cut-and-paste code */
Lots of commented-out lines here:
function PageControls_OnChange(GUIComponent Sender) { local byte byteVal; //local bool boolVal; //local string outString; if (!Controller.bCurMenuInitialized) return; switch(Sender) { case sldMouseSensitivity: log("mouse sensitivity is "$sldMouseSensitivity.Value); //class'PlayerInput'.default.MouseSensitivity = sldMouseSensitivity.Value; //PlayerOwner().SetSensitivity( sldMouseSensitivity.Value ); //outString = PlayerOwner().ConsoleCommand("set ini:Engine.Engine.PlayerInput MouseSensitivity "$sldMouseSensitivity.Value); //log("outString is "$outString); //PlayerOwner().ConsoleCommand("set PlayerInput MouseSensitivity "$sldMouseSensitivity.Value ); break; case chkInvertMouse: //if (chkInvertMouse.bChecked) // boolVal = true; //else // boolVal = false; //class'PlayerInput'.default.bInvertMouse = boolVal; //PlayerOwner().ConsoleCommand("set PlayerInput bInvertMouse "$boolVal); break; case chkMouseSmoothing: //if (chkMouseSmoothing.bChecked) // byteVal = 1; //else byteVal = 0; //class'PlayerInput'.default.MouseSmoothingMode = byteVal; //PlayerOwner().ConsoleCommand("set PlayerInput MouseSmoothingMode "$byteVal); break; } }
Some more in LoadExistingKeys function:
/* Let both keysets be used. --ryan. // rename keys that are on the numpad to their other designation for ( i = 0; i < kNumControls; i++ ) { if ( BoundKey1[i] == 96 ) BoundKey1[i] = 45; else if ( BoundKey1[i] == 110 ) BoundKey1[i] = 46; else if ( BoundKey1[i] == 97 ) BoundKey1[i] = 35; else if ( BoundKey1[i] == 98 ) BoundKey1[i] = 40; else if ( BoundKey1[i] == 99 ) BoundKey1[i] = 34; else if ( BoundKey1[i] == 100 ) BoundKey1[1] = 37; else if ( BoundKey1[i] == 101 ) BoundKey1[i] = 12; else if ( BoundKey1[i] == 102 ) BoundKey1[i] = 39; else if ( BoundKey1[i] == 103 ) BoundKey1[i] = 36; else if ( BoundKey1[i] == 104 ) BoundKey1[i] = 38; else if ( BoundKey1[i] == 105 ) BoundKey1[i] = 33; if ( BoundKey2[i] == 96 ) BoundKey2[i] = 45; else if ( BoundKey2[i] == 110 ) BoundKey2[i] = 46; else if ( BoundKey2[i] == 97 ) BoundKey2[i] = 35; else if ( BoundKey2[i] == 98 ) BoundKey2[i] = 40; else if ( BoundKey2[i] == 99 ) BoundKey2[i] = 34; else if ( BoundKey2[i] == 100 ) BoundKey2[1] = 37; else if ( BoundKey2[i] == 101 ) BoundKey2[i] = 12; else if ( BoundKey2[i] == 102 ) BoundKey2[i] = 39; else if ( BoundKey2[i] == 103 ) BoundKey2[i] = 36; else if ( BoundKey2[i] == 104 ) BoundKey2[i] = 38; else if ( BoundKey2[i] == 105 ) BoundKey2[i] = 33; } */
ProcessKey function has some commented-out lines too. Most notably, one of the comments mentions Aspyr Media - the publisher of Mac version.
// Commented out so user can, uh, use these... --ryan. //|| ((KeyNo >= 0x30 ) && (KeyNo <= 0x39)) // number keys /* Commented out so keys can be seperated, at Aspyr's request. :/ --ryan. if ( KeyNo == 45 || KeyNo == 96 ) hackAddKey(45, 96); else if ( KeyNo == 46 || KeyNo == 110 ) hackAddKey(46, 110); else if ( KeyNo == 35 || KeyNo == 97 ) hackAddKey(35, 97); else if ( KeyNo == 40 || KeyNo == 98 ) hackAddKey(40, 98); else if ( KeyNo == 34 || KeyNo == 99 ) hackAddKey(34, 99); else if ( KeyNo == 37 || KeyNo == 100 ) hackAddKey(37, 100); else if ( KeyNo == 12 || KeyNo == 101 ) hackAddKey(12, 101); else if ( KeyNo == 39 || KeyNo == 102 ) hackAddKey(39, 102); else if ( KeyNo == 36 || KeyNo == 103 ) hackAddKey(36, 103); else if ( KeyNo == 38 || KeyNo == 104 ) hackAddKey(38, 104); else if ( KeyNo == 33 || KeyNo == 105 ) hackAddKey(33, 105); else { */ // }
More in hackRemoveKeyFromActions function:
/* Use both keysets. --ryan. if ( keyNo == 96 ) alternate = 45; else if ( keyNo == 45 ) alternate = 96; else if ( keyNo == 110 ) alternate = 46; else if ( keyNo == 46 ) alternate = 110; else if ( keyNo == 97 ) alternate = 35; else if ( keyNo == 35 ) alternate = 97; else if ( keyNo == 98 ) alternate = 40; else if ( keyNo == 40 ) alternate = 98; else if ( keyNo == 34 ) alternate = 99; else if ( keyNo == 99 ) alternate = 34; else if ( keyNo == 100 ) alternate = 37; else if ( keyNo == 37 ) alternate = 100; else if ( keyNo == 101 ) alternate = 12; else if ( keyNo == 12 ) alternate = 101; else if ( keyNo == 102 ) alternate = 39; else if ( keyNo == 39 ) alternate = 102; else if ( keyNo == 103 ) alternate = 36; else if ( keyNo == 104 ) alternate = 38; else if ( keyNo == 38 ) alternate = 104; else if ( keyNo == 105 ) alternate = 33; else if ( keyNo == 33 ) alternate = 105; */
A funny comment in several functions of PageControls class:
// ugly! but this is the only one
A funny comment in event Trigger of MysterioMissilePod class:
if ( Enemy == None ) { // just for testing (or to punish those who may wish to trigger us w/o first // setting our enemy! Enemy = EventInstigator; }
Bump function of MannedVanPawn class had a different physics. It's the function, that allows vans to hit Spidey.
//impulse = PunchMomentum*(Normal(Velocity) - Normal(bumpActor.Velocity)); impulse *= PunchMomentum;
Class KWPatrolSetup has some unimplemented patrol action:
enum ePatrolAction { ACTION_None, ACTION_Animate // Placeholder, not supported right now. };
There is also commented-out code:
//var() eOptionalOverride Periphery_UseNewPV; // [Disbled] Optional Peripheral Vision override. //var() float Periphery_New; // [Disabled] Whether to use Optional Peripheral Vision override. // var() float Odds_Walk; // Odds of Pawn walking *TO* this Waypoint. 0.0 = Always. // var() float Odds_WayPoint; // [Disabled] Odds of this Waypoint being used. // Returns true if the odds are right and the Pawn is set to begin walking. function bool ShouldWalk() { //if( CheckOdds( SetupPatrolInfo[ GetNextSetupPatrolInfo() ].Odds_Walk ) ) return SetupPatrolInfo[ CurrentSetupPatrolInfo ].ShouldWalkTo; //else // return false; } // The patrol is initiated via a Trigger. function Trigger( actor Other, Pawn EventInstigator ) { StartPatrol( Other, EventInstigator ); // Disable it, just in case. //Disable( 'Trigger' ); } /* else if( Pawn( PatrollingPawn ) == None ) { log( "** Patrol ** Warning: Pawn "$PatrollingPawn$" isn't descended from Pawn! Aborting." ); return; }*/
KWPatrolController class also has some commented-out code and TODOs:
// Abort if the Pawn is involved with a Cutscene. //if( KWCutController( Pawn.Controller ) != None ) //{ // log( "* Patrol * Warning: Pawn "$Pawn$" is already occupied with a cutscene. Aborting." ); // return; //} function StopMoving() { MoveTarget = none; MoveTimer = -1; Pawn.acceleration *= vect(0,0,1); // wierd way to go to a label from a function // gotoState(GetStateName(), 'stopAction'); } function BeginState() { // Set up the Pawn's Seeing/Hearing event polling. PatrolSetup.InitializeEventPolling(); // Set up the Pawn's new physics, if any type was explicitly set. PatrolSetup.SetPawnPhysics(); // Do I need this here? I'm not sure, yet. //KWPawn( Pawn ).bIsMainPlayer = false; } // HACK: assume for now that we're after spidey if ( Seen.Tag == 'SpideyPawn' ) { EndPatrol(); Enemy = Seen; // TODO: copy this to the paused controller? Disable('SeePlayer'); } /if( PendingController.IsA('KWHeroController' ) && Pawn.IsA( 'KWPawn' ) ) // KWHeroController( PendingController ).SwitchToPawn( KWPawn( Pawn ) ); //else
KWDebugRootWindow isn't w/o commented-out code as well:
function Created() { Super.Created(); //MenuBar = KWDebugMenuBar(CreateWindow(class'KWDebugMenuBar', 50, 0, 500, 16)); //MenuBar.HideWindow(); Resized(); } if (!bAllowConsole) { //Root.GetPlayerOwner().ClientOpenMenu("Webhead.Frontend"); //MenuBar.ShowWindow(); } function EndState() { log("ending state UWindows"); //Root.GetPlayerOwner().ClientCloseMenu(); //MenuBar.HideWindow(); Super.EndState(); }
There's some weird todo for GenericFemalePawn class... Honestly, the full function is weird.
function name GetWeaponBoneFor(Inventory I) { // TODO: make the props class Inventory so we can decide left/right here return 'FemaleSkirtSuit_Body:R_gadget'; }
The functionality to save Spidey's health and position was removed from GameState class:
if( mSpideyDied > 0 || InMissionMode() ) { //log("***setting spidey's health to "$mSpideyHealth); //spidey.Health = mSpideyHealth; spidey.MaxHealth = mSpideyMaxHealth; spidey.Health = mSpideyMaxHealth; //log("***setting spidey's location to "$mSpideyLocation.X$", "$mSpideyLocation.Y$", "$mSpideyLocation.Z); //spidey.SetLocation(mSpideyLocation); }
This class also has this commented-out function:
//function string GetMapTitle(int index) //{ // return Localize( GetLevelPrefix(index), "Name", "maps" ); //}
This code is in the Frontend class. Initially, if you pressed "start new game" button on the main menu screen, the game would just load the most recent save. However, in the final game it says that you need to free some save slots to do this:
if ( nGames > 8 ) { // load most recently saved game log("loading most recently saved game"); //curState.LoadGame(lastGameIndex); //curMap = curState.GetMapFileName(lastGameIndex)$"?Game=Webhead.WebheadGameInfo"; Controller.OpenMenu("WebHead.PageNewGameMessage"); }
This function wasn't used. Judging by the name, it would activate "Extras" button you could find in the files of the demo. However, it doesn't even look to do anything remotely similar here at all...
function activateExtras(bool val) { btnCredits.bVisible = val; btnCredits.bAcceptsInput = val; }
Cop01Pawn has functionality to take damage and duck after it. Even run away, as was noticed after experimenting. However, it's not likely you can trigger it in the game casually, so it should be here.
/** * overridden to keep cops alive */ function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class<DamageType> damageType) { if ( Health <= Damage ) Health += Damage; super.TakeDamage( Damage, instigatedBy, hitLocation, momentum, damageType ); GotoState('Ducking'); } state Ducking { function BeginState() { IdleAnim = 'DuckFromGunShoot01Idle'; SetTimer(DuckUpdateTime, false); } function EndState() { PickRestIdle(); } function Timer() { if ( Level.TimeSeconds > LastPainTime + DuckTimeout) GotoState(, 'end'); else SetTimer(DuckUpdateTime, false); } end: ForcePlayBaseAnim('DuckFromGunShoot01Part2', 1.0); FinishAnim(0); ReleaseBaseAnim(); GotoState(''); begin: ForcePlayBaseAnim('DuckFromGunShoot01Part1', 1.0); FinishAnim(0); }
Commented-out code in state Waiting of ArmShowDownController class. It's for Ock's tentacles...
function BeginState() { //enable( 'SeePlayer' ); //SetCombatTimer(); } function Timer() { //enable( 'SeePlayer' ); //SetCombatTimer(); //FindSpidey(); } event Tick( float deltaTime ) { //log("I am waiting"); //if ( PickUpActor != None ) // GotoState('PickupState'); }