This is a sub-page of Spider-Man 2 (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:
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! <pre style='white-space: pre-wrap; overflow: auto; height: 200px > // 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. <pre style='white-space: pre-wrap; overflow: auto; height: 200px > // ==================================================================== // 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: <pre style='white-space: pre-wrap; overflow: auto; height: 200px > 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: <pre style='white-space: pre-wrap; overflow: auto; > [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: <pre style='white-space: pre-wrap; overflow: auto; height: 200px > // ==================================================================== // 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: <pre style='white-space: pre-wrap; overflow: auto; > 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: <pre style='white-space: pre-wrap; overflow: auto; height: 200px > 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: <pre style='white-space: pre-wrap; overflow: auto; > // 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: <pre style='white-space: pre-wrap; overflow: auto; height:200px > 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... <pre style='white-space: pre-wrap; overflow: auto; height:200px > // 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, then 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! <pre style='white-space: pre-wrap; overflow: auto; height: 200px > /**
* 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)
{
//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. <pre style='white-space: pre-wrap; overflow: auto; > 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. <pre style='white-space: pre-wrap; overflow: auto; height: 200px > 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(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. <pre style='white-space: pre-wrap; overflow: auto; height: 200px > 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. <pre style='white-space: pre-wrap; overflow: auto;> /*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: <pre style='white-space: pre-wrap; overflow: auto; > // 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? <pre style='white-space: pre-wrap; overflow: auto; > //-------------------------------------------------------------------------------------------------- // function TripTarget() //-------------------------------------------------------------------------------------------------- function TripTarget() {
}
Another commented-out exec function to stop adrenaline whenever you want: <pre style='white-space: pre-wrap; overflow: auto; > //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. <pre style='white-space: pre-wrap; overflow: auto; > //----------------------------------------------------------------------------- // 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: <pre style='white-space: pre-wrap; overflow: auto; > 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: <pre style='white-space: pre-wrap; overflow: auto; > //---------------------------------------------------------------------------------------------- // exec function Adrenaline() //---------------------------------------------------------------------------------------------- // exec function Adrenaline() // { // if( !Spidey.IsOnWall() ) // Spidey.StartAdrenalineTiming(); // }
A code for unused danger sense block action: <pre style='white-space: pre-wrap; overflow: auto; > //---------------------------------------------------------------------------------------------- // 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... <pre style='white-space: pre-wrap; overflow: auto; height: 200px > // 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? <pre style='white-space: pre-wrap; overflow: auto; > 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. <pre style='white-space: pre-wrap; overflow: auto; > 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! <pre style='white-space: pre-wrap; overflow: auto; > // // 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: <pre style='white-space: pre-wrap; overflow: auto; > 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: <pre style='white-space: pre-wrap; overflow: auto; height:200px > /*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: <pre style='white-space: pre-wrap; overflow: auto; > case SS_WebFluid:
result = spidey.WebMeter;
break;
}
However, the actual code drawing it was commented-out: <pre style='white-space: pre-wrap; overflow: auto; > // 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: <pre style='white-space: pre-wrap; overflow: auto; > // function TakeDamage( int Damage, Pawn EventInstigator, vector HitLocation, vector Momentum, class
Looks like another workaround... <pre style='white-space: pre-wrap; overflow: auto; > // // 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: <pre style='white-space: pre-wrap; overflow: auto; height:200px > // // 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: <pre style='white-space: pre-wrap; overflow: auto; > // TODO: Implement this properly Pawn.Fire(); FinishAnim( 0 ); FinishAttack();
Commented-out code in PumaController class: <pre style='white-space: pre-wrap; overflow: auto; > //----------------------------------------------------------------------------- // 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: <pre style='white-space: pre-wrap; overflow: auto; > // 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. <pre style='white-space: pre-wrap; overflow: auto; height: 200px > /////////////////////////////////////////////////////////////////////////////// // 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... <pre style='white-space: pre-wrap; overflow: auto; > // // 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. <pre style='white-space: pre-wrap; overflow: auto; > // // 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: <pre style='white-space: pre-wrap; overflow: auto; > //TimeStamp( "BeginState()" ); // SpideyPlayerController( Enemy.Controller ).GotoState( 'PlayerRooted' ); Puma.AttackType = Puma.ComboAttack; Puma.UseCrouchIdle = false; Puma.Charging = false; StunnedTime = 0.0f;
Another interesting workaround: <pre style='white-space: pre-wrap; overflow: auto; > // // 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... <pre style='white-space: pre-wrap; overflow: auto; height: 200px > 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
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, 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, 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. <pre style='white-space: pre-wrap; overflow: auto; height: 200px > //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
//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: <pre style='white-space: pre-wrap; overflow: auto; > // 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... <pre style='white-space: pre-wrap; overflow: auto; > 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. <pre style='white-space: pre-wrap; overflow: auto; > // todo: apply thicker texture or increase drawscale Skins[0]=Texture'CombatTextures.HeadWeb.HeadWeb03';
Another todo is in the WebSwingPoint class: <pre style='white-space: pre-wrap; overflow: auto; > // TODO: Is there some way to get around using texture variables here?
Another "temporary" fix comment is in the WebheadPawn class: <pre style='white-space: pre-wrap; overflow: auto; > // 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: <pre style='white-space: pre-wrap; overflow: auto; > // force shadows off if in 16-bit mode. --ryan.
More commented-out code of the class: <pre style='white-space: pre-wrap; overflow: auto; height:200px > /* 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: <pre style='white-space: pre-wrap; overflow: auto; > //if (!Edit.bPendingFocus) // MyListBox.Hide();
//List.InternalOnClick(Sender); //Edit.SetFocus(none); //return true;
This code is commented-out in several functions of VillainControllerclass: <pre style='white-space: pre-wrap; overflow: auto; > //if ( VSize(goal) > run_radius ) // return false; // don't run *too* far
But there's more: <pre style='white-space: pre-wrap; overflow: auto; > /*if ( pointReachable(goal) ) {
Destination = goal;
return true;
}*/
The insides of this function of VanController class is commented-out: <pre style='white-space: pre-wrap; overflow: auto; > function SetCombatTimer() {
//SetTimer(1.2 - 0.09 * FMin(10, Skill), True);
}
And there's some commented-out strings in FindBestPathToward function: <pre style='white-space: pre-wrap; overflow: auto; > if ( (A == Enemy) && (A != None) ) {
//FailedHuntTime = Level.TimeSeconds;
//FailedHuntEnemy = Enemy;
}
Even more here: <pre style='white-space: pre-wrap; overflow: auto; height: 200px > 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. <pre style='white-space: pre-wrap; overflow: auto; > 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: <pre style='white-space: pre-wrap; overflow: auto; height: 200px > 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
//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: <pre style='white-space: pre-wrap; overflow: auto; > //if ( bStrafeDir ) // transverse_part *= -1; //bStrafeDir = !bStrafeDir;
//bForcedDirection = true; //StartTacticalTime = Level.TimeSeconds;
And one more todo: <pre style='white-space: pre-wrap; overflow: auto; > Pawn.Acceleration = vect(0,0,0); // TODO: figure out why this is necessary
Commented-out function in SuperThugController: <pre style='white-space: pre-wrap; overflow: auto;> function ReleaseSpidey() { // Thug.bCanBeWebbed = true; // Thug.bCanBeAttacked = true; // SpideyPlayerController( Enemy.Controller ).GotoState( 'PlayerWalking' ); }
In SpideyHud class this function could be executable (callable via console): <pre style='white-space: pre-wrap; overflow: auto; > /*exec*/ function SetHealth( int val ) {
local SpideyPawn spidey;
spidey = WebheadGameInfo(Level.Game).Player;
spidey.Health = val;
}
Some todo in SecurityCamera class: <pre style='white-space: pre-wrap; overflow: auto; > 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. <pre style='white-space: pre-wrap; overflow: auto; > // 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... <pre style='white-space: pre-wrap; overflow: auto; height: 300px > //-------------------------------------------------------------------------------------------------- // 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: <pre style='white-space: pre-wrap; overflow: auto; > // SAL - programmers know best //SpawnRadius = 30; //SpawnOffset = 5; //SpawnHeight = 800;
Some unemplemented functions in RhinoPawn class: <pre style='white-space: pre-wrap; overflow: auto; height:200px > /**
* 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: <pre style='white-space: pre-wrap; overflow: auto; > 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: <pre style='white-space: pre-wrap; overflow: auto; > // 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: <pre style='white-space: pre-wrap; overflow: auto; > // Pedestrians are immortal.
Commented-out code in PatternGunTurret class: <pre style='white-space: pre-wrap; overflow: auto; > if ( TimeAlongPath > TimeToTracePath ) {
//mDir = BACKWARD;
//TimeAlongPath = TimeToTracePath;
//curIndex = PathDist.Length;
TimeAlongPath *= -1.0;
}
Commented-out function in PageVideo class: <pre style='white-space: pre-wrap; overflow: auto; > //----------------------------------------------------------------------------- // 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... <pre style='white-space: pre-wrap; overflow: auto; > /*
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: <pre style='white-space: pre-wrap; overflow: auto; > /*
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: <pre style='white-space: pre-wrap; overflow: auto; height: 200px > 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: <pre style='white-space: pre-wrap; overflow: auto; height: 200px > /* 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. <pre style='white-space: pre-wrap; overflow: auto; height: 200px > // 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: <pre style='white-space: pre-wrap; overflow: auto; height: 200px > /* 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: <pre style='white-space: pre-wrap; overflow: auto; > // ugly! but this is the only one
A funny comment in event Trigger of MysterioMissilePod class: <pre style='white-space: pre-wrap; overflow: auto; > 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. <pre style='white-space: pre-wrap; overflow: auto; > //impulse = PunchMomentum*(Normal(Velocity) - Normal(bumpActor.Velocity)); impulse *= PunchMomentum;
Class KWPatrolSetup has some unimplemented patrol action: <pre style='white-space: pre-wrap; overflow: auto; > enum ePatrolAction {
ACTION_None,
ACTION_Animate // Placeholder, not supported right now.
};
There is also commented-out code: <pre style='white-space: pre-wrap; overflow: auto; height:200px > //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: <pre style='white-space: pre-wrap; overflow: auto; height: 200px > // 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: <pre style='white-space: pre-wrap; overflow: auto; height: 200px > 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. <pre style='white-space: pre-wrap; overflow: auto; > 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: <pre style='white-space: pre-wrap; overflow: auto; height: 200px > 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: <pre style='white-space: pre-wrap; overflow: auto; > //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: <pre style='white-space: pre-wrap; overflow: auto; > 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... <pre style='white-space: pre-wrap; overflow: auto; > 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. <pre style='white-space: pre-wrap; overflow: auto; height:200px > /**
* overridden to keep cops alive
*/
function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation,
Vector momentum, class 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... <pre style='white-space: pre-wrap; overflow: auto; height: 200px > function BeginState() {
//enable( 'SeePlayer' );
//SetCombatTimer();
}
function Timer() {
//enable( 'SeePlayer' );
//SetCombatTimer();
//FindSpidey();
}
event Tick( float deltaTime ) {
//log("I am waiting");
//if ( PickUpActor != None )
// GotoState('PickupState');
}