Files
source/Editor/EditorItems.cpp
T
ChrisL bdcc920c63 Build1936
Minor adjustments to some of the rendering functions to grant a bit more control to the code.
Adjusted the mouse areas for key and item popup windows.
Added the pocket capacity indicator to sector inventory when using NewInv mode.
Added the right click option to sector inventory so you can view/manipulate stacks and view item descriptions without first equiping an item.
Updated sector inventory so that capacity values appear in all pockets.  Not just currently filled one.
Updated sector inventory so that newly placed items could be right-clicked on for details.
Increased the mouse region for the various stack popups so there is always room to close the popup without activating the description box.
Fixed a bug in the sector inventory description panel that caused a crash when trying to remove an attachment.
Fixed sector inventory so you can add attachments to weapons.
Fixed some weight issues.
Fixed a crash resulting from right-clicking in sector inventory while Item Desc window was already open.
Fixed the "Active Squad" message so it displays the squad that is actually activated.
Fixed a bug that allowed you to start a new game in NewInv mode while in 640x480 resolution.
Updated the Item Description windows so you can load a gun as well as unload.
Added new CTRL+SHIFT+D hotkey to delete all items in sector.
Added new CTRL+SHIFT+S hotkey to sell all items in sector, assuming you have the ALT+LMB option turned on in your INI file.
Fixed a problem with sector inventory item description where the status did not appear for items that couldn't stack.
Fixed a problem with Nails so he always starts with his leather jacket, even if you don't buy his gear.
Incorporated a failsafe so that NewInv mode can't be selected, nor NIV saves loaded, when no custom data path is set.
Add new IC_BELTCLIP item class.
Update pocket definition restrictions to use bitwise compare.
Add failsafe so that you can't merge LBENODEs.
Reset VC2005 so it doesn't point to a specific drive.


git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@1936 3b4a5df2-a311-0410-b5c6-a8a6f20db521
2008-03-29 20:35:03 +00:00

1667 lines
47 KiB
C++

#ifdef PRECOMPILEDHEADERS
#include "Editor All.h"
#else
#include "builddefines.h"
#endif
#ifdef JA2EDITOR
#ifndef PRECOMPILEDHEADERS
#include <windows.h>
#include "tiledef.h"
#include "edit_sys.h"
#include "vsurface.h"
#include "vobject.h"
#include "mousesystem.h"
#include "Button System.h"
#include "wcheck.h"
#include "vsurface.h"
#include "input.h"
#include "sysutil.h"
#include "font.h"
#include "Font Control.h"
#include "editscreen.h"
#include "selectwin.h"
#include "video.h"
#include "vobject_blitters.h"
#include "interface panels.h"
#include "interface items.h"
#include "text.h"
#include "utilities.h"
#include "Action Items.h"
#include "World Items.h"
#include "EditorDefines.h"
#include "EditorItems.h"
#include "EditorMercs.h"
#include "weapons.h"
#include "Editor Taskbar Utils.h"
#include "wordwrap.h"
#include "Item Statistics.h"
#include "Simple Render Utils.h"
#include "worldman.h"
#include "random.h"
#include "Pits.h"
#include "keys.h"
#endif
#define NUMBER_TRIGGERS 27
#define PRESSURE_ACTION_ID (NUMBER_TRIGGERS - 1)
extern ITEM_POOL *gpEditingItemPool;
//Simply counts the number of items in the world. This is used for display purposes.
UINT16 CountNumberOfEditorPlacementsInWorld( UINT16 usEInfoIndex, UINT16 *pusQuantity ); //wrapper for the next three
UINT16 CountNumberOfItemPlacementsInWorld( UINT16 usItem, UINT16 *pusQuantity );
UINT16 CountNumberOfItemsWithFrequency( UINT16 usItem, INT8 bFrequency );
UINT16 CountNumberOfPressureActionsInWorld();
UINT16 CountNumberOfKeysOfTypeInWorld( UINT8 ubKeyID );
//Finds and selects the next item when right clicking on an item type. Only works if the
//item actually exists in the world.
void FindNextItemOfSelectedType(); //wrapper for the next four
void SelectNextTriggerWithFrequency( UINT16 usItem, INT8 bFrequency );
void SelectNextItemOfType( UINT16 usItem );
void SelectNextPressureAction();
void SelectNextKeyOfType( UINT8 ubKeyID );
INT32 giDefaultExistChance = 100;
typedef struct IPListNode
{
INT16 sGridNo;
struct IPListNode *next;
}IPListNode;
IPListNode *pIPHead = NULL;
IPListNode *gpCurrItemPoolNode = NULL;
ITEM_POOL *gpItemPool = NULL;
void BuildItemPoolList()
{
ITEM_POOL *temp;
IPListNode *tail = NULL;
UINT16 i;
KillItemPoolList();
for( i = 0; i < WORLD_MAX; i++ )
{
if( GetItemPoolFromGround( i, &temp ) )
{
if( !pIPHead )
{
pIPHead = (IPListNode*)MemAlloc( sizeof( IPListNode ) );
Assert( pIPHead );
tail = pIPHead;
}
else
{
tail->next = (IPListNode*)MemAlloc( sizeof( IPListNode ) );
Assert( tail->next );
tail = tail->next;
}
ShowItemCursor( i );
tail->sGridNo = i;
tail->next = NULL;
}
}
gpCurrItemPoolNode = pIPHead;
SpecifyItemToEdit( NULL, -1 );
}
void KillItemPoolList()
{
IPListNode *pIPCurr;
pIPCurr = pIPHead;
while( pIPCurr )
{
HideItemCursor( pIPCurr->sGridNo );
pIPHead = pIPHead->next;
MemFree( pIPCurr );
pIPCurr = pIPHead;
}
pIPHead = NULL;
}
//Contains global information about the editor items
//May be expanded to encapsulate the entire editor later.
EditorItemsInfo eInfo;
//Does some precalculations regarding the number of each item type, so that it
//isn't calculated every time a player changes categories.
void EntryInitEditorItemsInfo()
{
INT32 i;
INVTYPE *item;
eInfo.uiBuffer = 0;
eInfo.fKill = 0;
eInfo.fActive = 0;
eInfo.sWidth = 0;
eInfo.sHeight = 0;
eInfo.sScrollIndex = 0;
eInfo.sSelItemIndex = 0;
eInfo.sHilitedItemIndex = -1;
eInfo.sNumItems = 0;
eInfo.pusItemIndex = NULL;
if( eInfo.fGameInit )
{ //This only gets called one time in game execution.
memset( &eInfo, 0, sizeof( EditorItemsInfo ) );
eInfo.sHilitedItemIndex = -1;
eInfo.uiItemType = TBAR_MODE_ITEM_WEAPONS;
//Pre-calculate the number of each item type.
eInfo.sNumTriggers = NUMBER_TRIGGERS;
for( i=0; i < MAXITEMS; i++ )
{
if ( Item[i].usItemClass == 0 )
break;
item = &Item[i];
//if( Item[i].fFlags & ITEM_NOT_EDITOR )
// continue;
if( i == SWITCH || i == ACTION_ITEM )
{
}
else switch( item->usItemClass )
{
case IC_GUN:
case IC_BLADE:
case IC_THROWN:
case IC_LAUNCHER:
case IC_THROWING_KNIFE:
eInfo.sNumWeapons++;
break;
case IC_PUNCH:
if ( i != NOTHING )
{
eInfo.sNumWeapons++;
}
break;
case IC_AMMO:
eInfo.sNumAmmo++;
break;
case IC_ARMOUR:
eInfo.sNumArmour++;
break;
case IC_LBEGEAR:
eInfo.sLBEGear++;
break;
case IC_GRENADE:
case IC_BOMB:
eInfo.sNumExplosives++;
break;
case IC_MEDKIT:
case IC_KIT:
case IC_FACE:
case IC_MONEY:
case IC_BELTCLIP:
eInfo.sNumEquipment1++;
break;
case IC_MISC:
if( eInfo.sNumEquipment2 < eInfo.sNumEquipment3 )
eInfo.sNumEquipment2++;
else
eInfo.sNumEquipment3++;
break;
//case IC_KEY:
// eInfo.sNumKeys++;
// break;
}
}
eInfo.sNumKeys = NUM_KEYS;
}
}
void InitEditorItemsInfo(UINT32 uiItemType)
{
VSURFACE_DESC vs_desc;
UINT8 *pDestBuf, *pSrcBuf;
UINT32 uiSrcPitchBYTES, uiDestPitchBYTES;
INVTYPE *item;
SGPRect SaveRect, NewRect;
HVOBJECT hVObject;
UINT32 uiVideoObjectIndex;
UINT16 usUselessWidth, usUselessHeight;
INT16 sWidth, sOffset, sStart;
INT16 i, x, y;
UINT16 usCounter;
CHAR16 pStr[ 100 ];//, pStr2[ 100 ];
CHAR16 pItemName[SIZE_ITEM_NAME];
UINT8 ubBitDepth;
BOOLEAN fTypeMatch;
INT32 iEquipCount = 0;
// Check to make sure that there isn't already a valid eInfo
if( eInfo.fActive )
{
if( eInfo.uiItemType == uiItemType )
{ //User clicked on the same item classification -- ignore
return;
}
else
{ //User selected a different item classification -- delete it first.
ClearEditorItemsInfo();
ClearTaskbarRegion( iScreenWidthOffset + 100, 2 * iScreenHeightOffset + 360, iScreenWidthOffset + 480, 2 * iScreenHeightOffset + 440 );
}
}
else
{
//Clear the menu area, so that the buffer doesn't get corrupted.
ClearTaskbarRegion( iScreenWidthOffset + 100, 2 * iScreenHeightOffset + 360, iScreenWidthOffset + 480, 2 * iScreenHeightOffset + 440 );
}
EnableEditorRegion( ITEM_REGION_ID );
eInfo.uiItemType = uiItemType;
eInfo.fActive = TRUE;
//Begin initialization of data.
switch(uiItemType)
{
case TBAR_MODE_ITEM_WEAPONS:
eInfo.sNumItems = eInfo.sNumWeapons;
eInfo.sScrollIndex = eInfo.sSaveWeaponsScrollIndex;
eInfo.sSelItemIndex = eInfo.sSaveSelWeaponsIndex;
break;
case TBAR_MODE_ITEM_AMMO:
eInfo.sNumItems = eInfo.sNumAmmo;
eInfo.sScrollIndex = eInfo.sSaveAmmoScrollIndex;
eInfo.sSelItemIndex = eInfo.sSaveSelAmmoIndex;
break;
case TBAR_MODE_ITEM_ARMOUR:
eInfo.sNumItems = eInfo.sNumArmour;
eInfo.sScrollIndex = eInfo.sSaveArmourScrollIndex;
eInfo.sSelItemIndex = eInfo.sSaveSelArmourIndex;
break;
case TBAR_MODE_ITEM_LBEGEAR:
eInfo.sNumItems = eInfo.sLBEGear;
eInfo.sScrollIndex = eInfo.sSaveLBEScrollIndex;
eInfo.sSelItemIndex = eInfo.sSaveSelLBEIndex;
break;
case TBAR_MODE_ITEM_EXPLOSIVES:
eInfo.sNumItems = eInfo.sNumExplosives;
eInfo.sScrollIndex = eInfo.sSaveExplosivesScrollIndex;
eInfo.sSelItemIndex = eInfo.sSaveSelExplosivesIndex;
break;
case TBAR_MODE_ITEM_EQUIPMENT1:
eInfo.sNumItems = eInfo.sNumEquipment1;
eInfo.sScrollIndex = eInfo.sSaveEquipment1ScrollIndex;
eInfo.sSelItemIndex = eInfo.sSaveSelEquipment1Index;
break;
case TBAR_MODE_ITEM_EQUIPMENT2:
eInfo.sNumItems = eInfo.sNumEquipment2;
eInfo.sScrollIndex = eInfo.sSaveEquipment2ScrollIndex;
eInfo.sSelItemIndex = eInfo.sSaveSelEquipment2Index;
break;
case TBAR_MODE_ITEM_EQUIPMENT3:
eInfo.sNumItems = eInfo.sNumEquipment3;
eInfo.sScrollIndex = eInfo.sSaveEquipment3ScrollIndex;
eInfo.sSelItemIndex = eInfo.sSaveSelEquipment3Index;
break;
case TBAR_MODE_ITEM_TRIGGERS:
eInfo.sNumItems = eInfo.sNumTriggers;
eInfo.sScrollIndex = eInfo.sSaveTriggersScrollIndex;
eInfo.sSelItemIndex = eInfo.sSaveSelTriggersIndex;
break;
case TBAR_MODE_ITEM_KEYS:
eInfo.sNumItems = eInfo.sNumKeys;
eInfo.sScrollIndex = eInfo.sSaveKeysScrollIndex;
eInfo.sSelItemIndex = eInfo.sSaveSelKeysIndex;
break;
default:
//error
return;
}
//Allocate memory to store all the item pointers.
eInfo.pusItemIndex = (UINT16*)MemAlloc( sizeof(UINT16) * eInfo.sNumItems );
//Disable the appropriate scroll buttons based on the saved scroll index if applicable
//Left most scroll position
DetermineItemsScrolling();
//calculate the width of the buffer based on the number of items.
//every pair of items (odd rounded up) requires 60 pixels for width.
//the minimum buffer size is 420. Height is always 80 pixels.
eInfo.sWidth = (eInfo.sNumItems > 12) ? ((eInfo.sNumItems+1)/2)*60 : SCREEN_HEIGHT - 120;
eInfo.sHeight = 80;
// Create item buffer
GetCurrentVideoSettings( &usUselessWidth, &usUselessHeight, &ubBitDepth );
vs_desc.fCreateFlags = VSURFACE_CREATE_DEFAULT | VSURFACE_SYSTEM_MEM_USAGE;
vs_desc.usWidth = eInfo.sWidth;
vs_desc.usHeight = eInfo.sHeight;
vs_desc.ubBitDepth = ubBitDepth;
//!!!Memory check. Create the item buffer
if(!AddVideoSurface( &vs_desc, &eInfo.uiBuffer ))
{
eInfo.fKill = TRUE;
eInfo.fActive = FALSE;
return;
}
pDestBuf = LockVideoSurface(eInfo.uiBuffer, &uiDestPitchBYTES);
pSrcBuf = LockVideoSurface(FRAME_BUFFER, &uiSrcPitchBYTES);
//copy a blank chunk of the editor interface to the new buffer.
for( i=0; i<eInfo.sWidth; i+=60 )
{
Blt16BPPTo16BPP((UINT16 *)pDestBuf, uiDestPitchBYTES,
(UINT16 *)pSrcBuf, uiSrcPitchBYTES, 0+i, 0, iScreenWidthOffset + 100, 2 * iScreenHeightOffset + 360, 60, 80 );
}
UnLockVideoSurface(eInfo.uiBuffer);
UnLockVideoSurface(FRAME_BUFFER);
x = 0;
y = 0;
usCounter = 0;
NewRect.iTop = 0;
NewRect.iBottom = eInfo.sHeight;
NewRect.iLeft = 0;
NewRect.iRight = eInfo.sWidth;
GetClippingRect(&SaveRect);
SetClippingRect(&NewRect);
if( eInfo.uiItemType == TBAR_MODE_ITEM_KEYS )
{ //Keys use a totally different method for determining
for( i = 0; i < eInfo.sNumItems; i++ )
{
item = &Item[ KeyTable[ 0 ].usItem + LockTable[ i ].usKeyItem ];
uiVideoObjectIndex = GetInterfaceGraphicForItem( item );
GetVideoObject( &hVObject, uiVideoObjectIndex );
//Store these item pointers for later when rendering selected items.
eInfo.pusItemIndex[i] = KeyTable[ 0 ].usItem + LockTable[ i ].usKeyItem;
SetFont(SMALLCOMPFONT);
SetFontForeground( FONT_MCOLOR_WHITE );
SetFontDestBuffer( eInfo.uiBuffer, 0, 0, eInfo.sWidth, eInfo.sHeight, FALSE );
swprintf( pStr, L"%S", LockTable[ i ].ubEditorName );
DisplayWrappedString(x, (UINT16)(y+25), 60, 2, SMALLCOMPFONT, FONT_WHITE, pStr, FONT_BLACK, TRUE, CENTER_JUSTIFIED );
//Calculate the center position of the graphic in a 60 pixel wide area.
sWidth = hVObject->pETRLEObject[item->ubGraphicNum].usWidth;
sOffset = hVObject->pETRLEObject[item->ubGraphicNum].sOffsetX;
sStart = x + (60 - sWidth - sOffset*2) / 2;
BltVideoObjectOutlineFromIndex( eInfo.uiBuffer, uiVideoObjectIndex, item->ubGraphicNum, sStart, y+2, 0, FALSE );
//cycle through the various slot positions (0,0), (0,40), (60,0), (60,40), (120,0)...
if( y == 0 )
{
y = 40;
}
else
{
y = 0;
x += 60;
}
}
}
else for( i = 0; i < eInfo.sNumItems; i++ )
{
fTypeMatch = FALSE;
while( usCounter<MAXITEMS && !fTypeMatch )
{
if ( Item[usCounter].usItemClass == 0 )
break;
item = &Item[usCounter];
//if( Item[usCounter].fFlags & ITEM_NOT_EDITOR )
{
// usCounter++;
// continue;
}
if( eInfo.uiItemType == TBAR_MODE_ITEM_TRIGGERS )
{
if( i < PRESSURE_ACTION_ID )
usCounter = ( i % 2 ) ? ACTION_ITEM : SWITCH;
else
usCounter = ACTION_ITEM;
fTypeMatch = TRUE;
item = &Item[usCounter];
}
else switch( item->usItemClass )
{
case IC_GUN:
case IC_BLADE:
case IC_LAUNCHER:
case IC_THROWN:
case IC_THROWING_KNIFE:
fTypeMatch = eInfo.uiItemType == TBAR_MODE_ITEM_WEAPONS;
break;
case IC_PUNCH:
if ( i != NOTHING )
{
fTypeMatch = eInfo.uiItemType == TBAR_MODE_ITEM_WEAPONS;
}
else
{
fTypeMatch = FALSE;
}
break;
case IC_AMMO:
fTypeMatch = eInfo.uiItemType == TBAR_MODE_ITEM_AMMO;
break;
case IC_ARMOUR:
fTypeMatch = eInfo.uiItemType == TBAR_MODE_ITEM_ARMOUR;
break;
case IC_LBEGEAR:
fTypeMatch = eInfo.uiItemType == TBAR_MODE_ITEM_LBEGEAR;
break;
case IC_GRENADE:
case IC_BOMB:
fTypeMatch = eInfo.uiItemType == TBAR_MODE_ITEM_EXPLOSIVES;
break;
case IC_MEDKIT:
case IC_KIT:
case IC_FACE:
case IC_MONEY:
case IC_BELTCLIP:
fTypeMatch = eInfo.uiItemType == TBAR_MODE_ITEM_EQUIPMENT1;
break;
case IC_MISC:
if( usCounter == ACTION_ITEM || usCounter == SWITCH )
break;
if( iEquipCount < eInfo.sNumEquipment2 )
fTypeMatch = eInfo.uiItemType == TBAR_MODE_ITEM_EQUIPMENT2;
else
fTypeMatch = eInfo.uiItemType == TBAR_MODE_ITEM_EQUIPMENT3;
iEquipCount++;
break;
}
if( fTypeMatch )
{
uiVideoObjectIndex = GetInterfaceGraphicForItem( item );
GetVideoObject( &hVObject, uiVideoObjectIndex );
//Store these item pointers for later when rendering selected items.
eInfo.pusItemIndex[i] = usCounter;
SetFont(SMALLCOMPFONT);
SetFontForeground( FONT_MCOLOR_WHITE );
SetFontDestBuffer( eInfo.uiBuffer, 0, 0, eInfo.sWidth, eInfo.sHeight, FALSE );
if( eInfo.uiItemType != TBAR_MODE_ITEM_TRIGGERS )
{
LoadItemInfo( usCounter, pItemName, NULL );
swprintf( pStr, L"%s", pItemName );
}
else
{
if( i == PRESSURE_ACTION_ID )
{
swprintf( pStr, L"Pressure Action" );
}
else if( i < 2 )
{
if( usCounter == SWITCH )
swprintf( pStr, L"Panic Trigger1" );
else
swprintf( pStr, L"Panic Action1" );
}
else if( i < 4 )
{
if( usCounter == SWITCH )
swprintf( pStr, L"Panic Trigger2" );
else
swprintf( pStr, L"Panic Action2" );
}
else if( i < 6 )
{
if( usCounter == SWITCH )
swprintf( pStr, L"Panic Trigger3" );
else
swprintf( pStr, L"Panic Action3" );
}
else
{
if( usCounter == SWITCH )
swprintf( pStr, L"Trigger%d", (i-4)/2 );
else
swprintf( pStr, L"Action%d", (i-4)/2 );
}
}
DisplayWrappedString(x, (UINT16)(y+25), 60, 2, SMALLCOMPFONT, FONT_WHITE, pStr, FONT_BLACK, TRUE, CENTER_JUSTIFIED );
//Calculate the center position of the graphic in a 60 pixel wide area.
sWidth = hVObject->pETRLEObject[item->ubGraphicNum].usWidth;
sOffset = hVObject->pETRLEObject[item->ubGraphicNum].sOffsetX;
sStart = x + (60 - sWidth - sOffset*2) / 2;
if( sWidth )
{
BltVideoObjectOutlineFromIndex( eInfo.uiBuffer, uiVideoObjectIndex, item->ubGraphicNum, sStart, y+2, 0, FALSE );
}
//cycle through the various slot positions (0,0), (0,40), (60,0), (60,40), (120,0)...
if( y == 0 )
{
y = 40;
}
else
{
y = 0;
x += 60;
}
}
usCounter++;
}
}
SetFontDestBuffer( FRAME_BUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, FALSE );
SetClippingRect(&SaveRect);
gfRenderTaskbar = TRUE;
}
void DetermineItemsScrolling()
{
if( !eInfo.sScrollIndex )
DisableEditorButton( ITEMS_LEFTSCROLL );
else
EnableEditorButton( ITEMS_LEFTSCROLL );
//Right most scroll position. Calculated by taking every pair of numItems rounded up,
//and subtracting 7 (because a scroll index 0 is disabled if there are <=12 items,
//index 1 for <=14 items, index 2 for <=16 items...
if( eInfo.sScrollIndex == max( ((eInfo.sNumItems+1)/2)-6, 0 ) )
DisableEditorButton( ITEMS_RIGHTSCROLL );
else
EnableEditorButton( ITEMS_RIGHTSCROLL );
}
void RenderEditorItemsInfo()
{
UINT8 *pDestBuf, *pSrcBuf;
UINT32 uiSrcPitchBYTES, uiDestPitchBYTES;
INVTYPE *item;
HVOBJECT hVObject;
UINT32 uiVideoObjectIndex;
INT16 i;
INT16 minIndex, maxIndex;
INT16 sWidth, sOffset, sStart, x, y;
UINT16 usNumItems;
UINT16 usQuantity;
if(!eInfo.fActive)
{
return;
}
if( gusMouseXPos < (iScreenWidthOffset + 110) || gusMouseXPos > (iScreenWidthOffset + 480) || gusMouseYPos < (2 * iScreenHeightOffset + 360) || gusMouseYPos > (2 * iScreenHeightOffset + 440) )
{ //Mouse has moved out of the items display region -- so nothing can be highlighted.
eInfo.sHilitedItemIndex = -1;
}
pDestBuf = LockVideoSurface(FRAME_BUFFER, &uiDestPitchBYTES);
pSrcBuf = LockVideoSurface(eInfo.uiBuffer, &uiSrcPitchBYTES);
Blt16BPPTo16BPP((UINT16 *)pDestBuf, uiDestPitchBYTES,
(UINT16 *)pSrcBuf, uiSrcPitchBYTES, iScreenWidthOffset + 110, 2 * iScreenHeightOffset + 360, 60*eInfo.sScrollIndex, 0, 360, 80 );
UnLockVideoSurface(eInfo.uiBuffer);
UnLockVideoSurface(FRAME_BUFFER);
//calculate the min and max index that is currently shown. This determines
//if the highlighted and/or selected items are drawn with the outlines.
minIndex = eInfo.sScrollIndex * 2;
maxIndex = minIndex + 11;
//draw the hilighted item if applicable.
if( eInfo.sHilitedItemIndex >= minIndex && eInfo.sHilitedItemIndex <= maxIndex )
{
if( eInfo.pusItemIndex )
{
item = &Item[eInfo.pusItemIndex[eInfo.sHilitedItemIndex]];
uiVideoObjectIndex = GetInterfaceGraphicForItem( item );
GetVideoObject( &hVObject, uiVideoObjectIndex );
x = iScreenWidthOffset + (eInfo.sHilitedItemIndex/2 - eInfo.sScrollIndex)*60 + 110;
y = 2 * iScreenHeightOffset + 360 + (eInfo.sHilitedItemIndex % 2) * 40;
sWidth = hVObject->pETRLEObject[item->ubGraphicNum].usWidth;
sOffset = hVObject->pETRLEObject[item->ubGraphicNum].sOffsetX;
sStart = x + (60 - sWidth - sOffset*2) / 2;
if( sWidth )
{
BltVideoObjectOutlineFromIndex( FRAME_BUFFER, uiVideoObjectIndex, item->ubGraphicNum, sStart, y+2, Get16BPPColor(FROMRGB(250, 250, 0)), TRUE );
}
}
}
//draw the selected item
if( eInfo.sSelItemIndex >= minIndex && eInfo.sSelItemIndex <= maxIndex )
{
if( eInfo.pusItemIndex )
{
item = &Item[eInfo.pusItemIndex[eInfo.sSelItemIndex]];
uiVideoObjectIndex = GetInterfaceGraphicForItem( item );
GetVideoObject( &hVObject, uiVideoObjectIndex );
x = iScreenWidthOffset + (eInfo.sSelItemIndex/2 - eInfo.sScrollIndex)*60 + 110;
y = 2 * iScreenHeightOffset + 360 + (eInfo.sSelItemIndex % 2) * 40;
sWidth = hVObject->pETRLEObject[item->ubGraphicNum].usWidth;
sOffset = hVObject->pETRLEObject[item->ubGraphicNum].sOffsetX;
sStart = x + (60 - sWidth - sOffset*2) / 2;
if( sWidth )
{
BltVideoObjectOutlineFromIndex( FRAME_BUFFER, uiVideoObjectIndex, item->ubGraphicNum, sStart, y+2, Get16BPPColor(FROMRGB(250, 0, 0)), TRUE );
}
}
}
//draw the numbers of each visible item that currently resides in the world.
maxIndex = min( maxIndex, eInfo.sNumItems-1 );
for( i = minIndex; i <= maxIndex; i++ )
{
usNumItems = CountNumberOfEditorPlacementsInWorld( i, &usQuantity );
if( usNumItems )
{
x = iScreenWidthOffset + (i/2 - eInfo.sScrollIndex)*60 + 110;
y = 2 * iScreenHeightOffset + 360 + (i % 2) * 40;
SetFont( FONT10ARIAL );
SetFontForeground( FONT_YELLOW );
SetFontShadow( FONT_NEARBLACK );
if( usNumItems == usQuantity )
mprintf( x + 12, y + 4, L"%d", usNumItems );
else
mprintf( x + 12, y + 4, L"%d(%d)", usNumItems, usQuantity );
}
}
}
void ClearEditorItemsInfo()
{
if( eInfo.uiBuffer )
{
DeleteVideoSurfaceFromIndex( eInfo.uiBuffer );
eInfo.uiBuffer = 0;
}
if( eInfo.pusItemIndex )
{
MemFree( eInfo.pusItemIndex );
eInfo.pusItemIndex = NULL;
}
DisableEditorRegion( ITEM_REGION_ID );
eInfo.fKill = 0;
eInfo.fActive = 0;
eInfo.sWidth = 0;
eInfo.sHeight = 0;
eInfo.sNumItems = 0;
//save the highlighted selections
switch( eInfo.uiItemType )
{
case TBAR_MODE_ITEM_WEAPONS:
eInfo.sSaveSelWeaponsIndex = eInfo.sSelItemIndex;
eInfo.sSaveWeaponsScrollIndex = eInfo.sScrollIndex;
break;
case TBAR_MODE_ITEM_AMMO:
eInfo.sSaveSelAmmoIndex = eInfo.sSelItemIndex;
eInfo.sSaveAmmoScrollIndex = eInfo.sScrollIndex;
break;
case TBAR_MODE_ITEM_ARMOUR:
eInfo.sSaveSelArmourIndex = eInfo.sSelItemIndex;
eInfo.sSaveArmourScrollIndex = eInfo.sScrollIndex;
break;
case TBAR_MODE_ITEM_LBEGEAR:
eInfo.sSaveSelLBEIndex = eInfo.sSelItemIndex;
eInfo.sSaveLBEScrollIndex = eInfo.sScrollIndex;
break;
case TBAR_MODE_ITEM_EXPLOSIVES:
eInfo.sSaveSelExplosivesIndex = eInfo.sSelItemIndex;
eInfo.sSaveExplosivesScrollIndex = eInfo.sScrollIndex;
break;
case TBAR_MODE_ITEM_EQUIPMENT1:
eInfo.sSaveSelEquipment1Index = eInfo.sSelItemIndex;
eInfo.sSaveEquipment1ScrollIndex = eInfo.sScrollIndex;
break;
case TBAR_MODE_ITEM_EQUIPMENT2:
eInfo.sSaveSelEquipment2Index = eInfo.sSelItemIndex;
eInfo.sSaveEquipment2ScrollIndex = eInfo.sScrollIndex;
break;
case TBAR_MODE_ITEM_EQUIPMENT3:
eInfo.sSaveSelEquipment3Index = eInfo.sSelItemIndex;
eInfo.sSaveEquipment3ScrollIndex = eInfo.sScrollIndex;
break;
case TBAR_MODE_ITEM_TRIGGERS:
eInfo.sSaveSelTriggersIndex = eInfo.sSelItemIndex;
eInfo.sSaveTriggersScrollIndex = eInfo.sScrollIndex;
break;
case TBAR_MODE_ITEM_KEYS:
eInfo.sSaveSelKeysIndex = eInfo.sSelItemIndex;
eInfo.sSaveKeysScrollIndex = eInfo.sScrollIndex;
break;
}
}
void HandleItemsPanel( UINT16 usScreenX, UINT16 usScreenY, INT8 bEvent )
{
INT16 sIndex;
UINT16 usQuantity;
//Calc base index from scrolling index
sIndex = eInfo.sScrollIndex * 2;
//Determine if the index is in the first row or second row from mouse YPos.
if( usScreenY >= (2 * iScreenHeightOffset + 400) )
sIndex++;
//Add the converted mouse's XPos into a relative index;
//Calc: starting from 110, for every 60 pixels, add 2 to the index
sIndex += ((usScreenX-110-iScreenWidthOffset)/60) * 2;
switch( bEvent )
{
case GUI_MOVE_EVENT:
if( sIndex < eInfo.sNumItems )
{
if( eInfo.sHilitedItemIndex != sIndex )
gfRenderTaskbar = TRUE;
//this index will now highlight in yellow.
eInfo.sHilitedItemIndex = sIndex;
}
break;
case GUI_LCLICK_EVENT:
if( sIndex < eInfo.sNumItems )
{
//this index will now highlight in red.
if( eInfo.sSelItemIndex != sIndex )
gfRenderTaskbar = TRUE;
eInfo.sSelItemIndex = sIndex;
if( gfMercGetItem )
{
gfMercGetItem = FALSE;
gusMercsNewItemIndex = eInfo.pusItemIndex[ eInfo.sSelItemIndex ];
SetMercEditingMode( MERC_INVENTORYMODE );
ClearEditorItemsInfo();
}
}
break;
case GUI_RCLICK_EVENT:
if( gfMercGetItem )
{
gfMercGetItem = FALSE;
gusMercsNewItemIndex = 0xffff;
SetMercEditingMode( MERC_INVENTORYMODE );
ClearEditorItemsInfo();
}
else if( sIndex < eInfo.sNumItems )
{
eInfo.sSelItemIndex = sIndex;
gfRenderTaskbar = TRUE;
if( CountNumberOfEditorPlacementsInWorld( eInfo.sSelItemIndex, &usQuantity ) )
{
FindNextItemOfSelectedType();
}
}
break;
}
}
void ShowItemCursor( INT32 iMapIndex )
{
LEVELNODE *pNode;
pNode = gpWorldLevelData[ iMapIndex ].pTopmostHead;
while( pNode )
{
if( pNode->usIndex == SELRING )
return;
pNode = pNode->pNext;
}
AddTopmostToTail( iMapIndex, SELRING1 );
}
void HideItemCursor( INT32 iMapIndex )
{
RemoveTopmost( iMapIndex, SELRING1 );
}
BOOLEAN TriggerAtGridNo( INT16 sGridNo )
{
ITEM_POOL *pItemPool;
if( !GetItemPoolFromGround( sGridNo, &pItemPool ) )
{
return FALSE;
}
while( pItemPool )
{
if( gWorldItems[ pItemPool->iItemIndex ].object.usItem == SWITCH )
{
return TRUE;
}
pItemPool = pItemPool->pNext;
}
return FALSE;
}
void AddSelectedItemToWorld( INT16 sGridNo )
{
OBJECTTYPE *pObject;
INVTYPE *pItem;
ITEM_POOL *pItemPool;
INT32 iItemIndex;
INT8 bVisibility = INVISIBLE;
BOOLEAN fFound = FALSE;
IPListNode *pIPCurr, *pIPPrev;
UINT16 usFlags;
//Extract the currently selected item.
SpecifyItemToEdit( NULL, -1 );
if( eInfo.uiItemType == TBAR_MODE_ITEM_KEYS )
{
CreateKeyObject( &gTempObject, 1, (UINT8)eInfo.sSelItemIndex );
}
else
{
CreateItem( eInfo.pusItemIndex[eInfo.sSelItemIndex], 100, &gTempObject );
}
usFlags = 0;
switch( gTempObject.usItem )
{
case MINE:
if ( bVisibility == BURIED )
{
usFlags |= WORLD_ITEM_ARMED_BOMB;
}
break;
case MONEY:
case SILVER:
case GOLD:
gTempObject[0]->data.objectStatus = 100;
gTempObject[0]->data.money.uiMoneyAmount = 100 + Random( 19901 );
break;
case OWNERSHIP:
gTempObject[0]->data.owner.ubOwnerProfile = NO_PROFILE;
bVisibility = BURIED;
break;
case SWITCH:
if( TriggerAtGridNo( sGridNo ) )
{ //Restricted to one action per gridno.
return;
}
bVisibility = BURIED;
gTempObject[0]->data.objectStatus = 100;
gTempObject[0]->data.misc.ubBombOwner = 1;
if( eInfo.sSelItemIndex < 2 )
gTempObject[0]->data.misc.bFrequency = PANIC_FREQUENCY;
else if( eInfo.sSelItemIndex < 4 )
gTempObject[0]->data.misc.bFrequency = PANIC_FREQUENCY_2;
else if( eInfo.sSelItemIndex < 6 )
gTempObject[0]->data.misc.bFrequency = PANIC_FREQUENCY_3;
else
gTempObject[0]->data.misc.bFrequency = (INT8)(FIRST_MAP_PLACED_FREQUENCY + (eInfo.sSelItemIndex-4) / 2);
usFlags |= WORLD_ITEM_ARMED_BOMB;
break;
case ACTION_ITEM:
bVisibility = BURIED;
gTempObject[0]->data.objectStatus = 100;
gTempObject[0]->data.misc.ubBombOwner = 1;
gTempObject[0]->data.bTrap = gbDefaultBombTrapLevel;
if( eInfo.sSelItemIndex < PRESSURE_ACTION_ID )
{
gTempObject[0]->data.misc.bDetonatorType = BOMB_REMOTE;
if( eInfo.sSelItemIndex < 2 )
gTempObject[0]->data.misc.bFrequency = PANIC_FREQUENCY;
else if( eInfo.sSelItemIndex < 4 )
gTempObject[0]->data.misc.bFrequency = PANIC_FREQUENCY_2;
else if( eInfo.sSelItemIndex < 6 )
gTempObject[0]->data.misc.bFrequency = PANIC_FREQUENCY_3;
else
gTempObject[0]->data.misc.bFrequency = (INT8)(FIRST_MAP_PLACED_FREQUENCY + (eInfo.sSelItemIndex-4) / 2);
}
else
{
gTempObject[0]->data.misc.bDetonatorType = BOMB_PRESSURE;
gTempObject[0]->data.misc.bDelay = 0;
}
ChangeActionItem( &gTempObject, gbActionItemIndex );
gTempObject.fFlags |= OBJECT_ARMED_BOMB;
if( gbActionItemIndex == ACTIONITEM_SMPIT )
Add3X3Pit( sGridNo );
else if( gbActionItemIndex == ACTIONITEM_LGPIT )
Add5X5Pit( sGridNo );
usFlags |= WORLD_ITEM_ARMED_BOMB;
break;
}
pObject = InternalAddItemToPool( &sGridNo, &gTempObject, bVisibility, 0, usFlags, 0, -1, &iItemIndex );
if( gTempObject.usItem != OWNERSHIP )
{
gWorldItems[ iItemIndex ].ubNonExistChance = (UINT8)(100 - giDefaultExistChance);
}
else
{
gWorldItems[ iItemIndex ].ubNonExistChance = 0;
}
pItem = &( Item[ pObject->usItem ] );
if( pItem->usItemClass == IC_AMMO )
{
if (Random( 2 ))
{
(*pObject)[0]->data.ubShotsLeft = Magazine[ pItem->ubClassIndex ].ubMagSize;
}
else
{
(*pObject)[0]->data.ubShotsLeft = (UINT8) Random( Magazine[ pItem->ubClassIndex ].ubMagSize );
}
}
else
{
(*pObject)[0]->data.objectStatus = (INT8)(70 + Random( 26 ));
}
if( pItem->usItemClass & IC_GUN )
{
if ( pObject->usItem == ROCKET_LAUNCHER )
{
(*pObject)[0]->data.gun.ubGunShotsLeft = 1;
}
else
{
(*pObject)[0]->data.gun.ubGunShotsLeft = (UINT8)(Random( Weapon[ pObject->usItem ].ubMagSize ));
}
}
if( !GetItemPoolFromGround( sGridNo, &pItemPool ) )
Assert( 0 );
while( pItemPool )
{
if( &(gWorldItems[ pItemPool->iItemIndex ].object) == pObject )
{
fFound = TRUE;
//ShowSelectedItem();
break;
}
pItemPool = pItemPool->pNext;
}
Assert( fFound );
gpItemPool = pItemPool;
SpecifyItemToEdit( pObject, sGridNo );
//Get access to the itempool.
//search for a current node in list containing same mapindex
pIPCurr = pIPHead;
pIPPrev = NULL;
while( pIPCurr )
{
pIPPrev = pIPCurr;
if( pIPCurr->sGridNo == sGridNo )
{
//found one, so we don't need to add it
gpCurrItemPoolNode = pIPCurr;
return;
}
pIPCurr = pIPCurr->next;
}
//there isn't one, so we will add it now.
ShowItemCursor( sGridNo );
if( pIPPrev )
{
pIPPrev->next = (IPListNode*)MemAlloc( sizeof( IPListNode ) );
Assert( pIPPrev->next );
pIPPrev = pIPPrev->next;
pIPPrev->next = NULL;
pIPPrev->sGridNo = sGridNo;
gpCurrItemPoolNode = pIPPrev;
}
else
{
pIPHead = (IPListNode*)MemAlloc( sizeof( IPListNode ) );
Assert( pIPHead );
pIPHead->next = NULL;
pIPHead->sGridNo = sGridNo;
gpCurrItemPoolNode = pIPHead;
}
}
void HandleRightClickOnItem( INT16 sGridNo )
{
ITEM_POOL *pItemPool;
IPListNode *pIPCurr;
if( gsItemGridNo == sGridNo )
{ //Clicked on the same gridno as the selected item. Automatically select the next
//item in the same pool.
pItemPool = gpItemPool->pNext;
if( !pItemPool )
{ //currently selected item was last node, so select the head node even if it is the same.
GetItemPoolFromGround( sGridNo, &pItemPool );
}
}
else if( !GetItemPoolFromGround( sGridNo, &pItemPool ) )
{
//possibly relocate selected item to this gridno?
return;
}
gpItemPool = pItemPool;
//set up the item pool pointer to point to the same mapindex node
pIPCurr = pIPHead;
gpCurrItemPoolNode = NULL;
while( pIPCurr )
{
if( pIPCurr->sGridNo == sGridNo )
{
gpCurrItemPoolNode = pIPCurr;
break;
}
pIPCurr = pIPCurr->next;
}
Assert( gpCurrItemPoolNode );
SpecifyItemToEdit( &gWorldItems[ gpItemPool->iItemIndex ].object, gpItemPool->sGridNo );
}
extern void DeleteSelectedMercsItem();
void DeleteSelectedItem()
{
SpecifyItemToEdit( NULL, -1 );
//First, check to see if there even is a currently selected item.
if( iCurrentTaskbar == TASK_MERCS )
{
DeleteSelectedMercsItem();
return;
}
if( gpItemPool )
{ //Okay, we have a selected item...
INT16 sGridNo;
//save the mapindex
if( gpItemPool->pNext )
{
SpecifyItemToEdit( &gWorldItems[ gpItemPool->pNext->iItemIndex ].object, gpItemPool->sGridNo );
}
sGridNo = gpItemPool->sGridNo;
//remove the item
if( gWorldItems[ gpItemPool->iItemIndex ].object.usItem == ACTION_ITEM )
{
if( gWorldItems[ gpItemPool->iItemIndex ].object[0]->data.misc.bActionValue == ACTION_ITEM_SMALL_PIT )
Remove3X3Pit( gWorldItems[ gpItemPool->iItemIndex ].sGridNo );
else if( gWorldItems[ gpItemPool->iItemIndex ].object[0]->data.misc.bActionValue == ACTION_ITEM_LARGE_PIT )
Remove5X5Pit( gWorldItems[ gpItemPool->iItemIndex ].sGridNo );
}
if( gpEditingItemPool == gpItemPool )
gpEditingItemPool = NULL;
RemoveItemFromPool( sGridNo, gpItemPool->iItemIndex, 0 );
gpItemPool = NULL;
//determine if there are still any items at this location
if( !GetItemPoolFromGround( sGridNo, &gpItemPool ) )
{ //no items left, so remove the node from the list.
IPListNode *pIPPrev, *pIPCurr;
pIPCurr = pIPHead;
pIPPrev = NULL;
while( pIPCurr )
{
if( pIPCurr->sGridNo == sGridNo )
{
if( pIPPrev ) //middle of list
pIPPrev->next = pIPCurr->next;
else //head of list
pIPHead = pIPHead->next;
//move the curr item pool to the next one.
if( pIPCurr->next )
gpCurrItemPoolNode = pIPCurr->next;
else
gpCurrItemPoolNode = pIPHead;
if( gpCurrItemPoolNode )
{
GetItemPoolFromGround( gpCurrItemPoolNode->sGridNo, &gpItemPool );
Assert( gpItemPool );
}
//remove node
HideItemCursor( sGridNo );
MemFree( pIPCurr );
pIPCurr = NULL;
return;
}
pIPPrev = pIPCurr;
pIPCurr = pIPCurr->next;
}
}
}
}
void ShowSelectedItem()
{
if( gpItemPool )
{
gpItemPool->bVisible = INVISIBLE;
gWorldItems[ gpItemPool->iItemIndex ].bVisible = INVISIBLE;
}
}
void HideSelectedItem()
{
if( gpItemPool )
{
gpItemPool->bVisible = HIDDEN_ITEM;
gWorldItems[ gpItemPool->iItemIndex ].bVisible = HIDDEN_ITEM;
}
}
void SelectNextItemPool()
{
if( !gpCurrItemPoolNode )
return;
//remove the current hilight.
if( gpItemPool )
{
MarkMapIndexDirty( gpItemPool->sGridNo );
}
//go to the next node. If at end of list, choose pIPHead
if( gpCurrItemPoolNode->next )
gpCurrItemPoolNode = gpCurrItemPoolNode->next;
else
gpCurrItemPoolNode = pIPHead;
//get the item pool at this node's gridno.
GetItemPoolFromGround( gpCurrItemPoolNode->sGridNo, &gpItemPool );
MarkMapIndexDirty( gpItemPool->sGridNo );
SpecifyItemToEdit( &gWorldItems[ gpItemPool->iItemIndex ].object, gpItemPool->sGridNo );
if( gsItemGridNo != -1 )
{
CenterScreenAtMapIndex( gsItemGridNo );
}
}
void SelectNextItemInPool()
{
if( gpItemPool )
{
if( gpItemPool->pNext )
{
gpItemPool = gpItemPool->pNext;
}
else
{
GetItemPoolFromGround( gpItemPool->sGridNo, &gpItemPool );
}
SpecifyItemToEdit( &gWorldItems[ gpItemPool->iItemIndex ].object, gpItemPool->sGridNo );
MarkWorldDirty();
}
}
void SelectPrevItemInPool()
{
if( gpItemPool )
{
if( gpItemPool->pPrev )
{
gpItemPool = gpItemPool->pPrev;
}
else
{
GetItemPoolFromGround( gpItemPool->sGridNo, &gpItemPool );
while( gpItemPool->pNext )
{
gpItemPool = gpItemPool->pNext;
}
}
SpecifyItemToEdit( &gWorldItems[ gpItemPool->iItemIndex ].object, gpItemPool->sGridNo );
MarkWorldDirty();
}
}
void FindNextItemOfSelectedType()
{
UINT16 usItem;
usItem = eInfo.pusItemIndex[ eInfo.sSelItemIndex ];
if( usItem == ACTION_ITEM || usItem == SWITCH )
{
if( eInfo.sSelItemIndex < PRESSURE_ACTION_ID )
{
INT8 bFrequency;
if( eInfo.sSelItemIndex < 2 )
bFrequency = PANIC_FREQUENCY;
else if( eInfo.sSelItemIndex < 4 )
bFrequency = PANIC_FREQUENCY_2;
else if( eInfo.sSelItemIndex < 6 )
bFrequency = PANIC_FREQUENCY_3;
else
bFrequency = (INT8)(FIRST_MAP_PLACED_FREQUENCY + (eInfo.sSelItemIndex-4) / 2);
SelectNextTriggerWithFrequency( usItem, bFrequency );
}
else
{
SelectNextPressureAction();
}
}
else if( Item[ usItem ].usItemClass == IC_KEY )
{
SelectNextKeyOfType( (UINT8)eInfo.sSelItemIndex );
}
else
{
SelectNextItemOfType( usItem );
}
}
void SelectNextItemOfType( UINT16 usItem )
{
IPListNode *curr;
OBJECTTYPE *pObject;
if( gpItemPool )
{
curr = pIPHead;
while( curr )
{ //skip quickly to the same gridno as the item pool
if( curr->sGridNo == gWorldItems[ gpItemPool->iItemIndex ].sGridNo )
{
gpItemPool = gpItemPool->pNext;
while( gpItemPool )
{
pObject = &gWorldItems[ gpItemPool->iItemIndex ].object;
if( pObject->usItem == usItem )
{
SpecifyItemToEdit( pObject, gWorldItems[ gpItemPool->iItemIndex ].sGridNo );
CenterScreenAtMapIndex( gsItemGridNo );
return; //success! (another item in same itempool)
}
gpItemPool = gpItemPool->pNext;
}
curr = curr->next;
break;
}
curr = curr->next;
}
while( curr )
{ //search to the end of the list
GetItemPoolFromGround( curr->sGridNo, &gpItemPool );
while( gpItemPool )
{
pObject = &gWorldItems[ gpItemPool->iItemIndex ].object;
if( pObject->usItem == usItem )
{
SpecifyItemToEdit( pObject, gWorldItems[ gpItemPool->iItemIndex ].sGridNo );
CenterScreenAtMapIndex( gsItemGridNo );
return; //success! (found another item before reaching the end of the list)
}
gpItemPool = gpItemPool->pNext;
}
curr = curr->next;
}
}
curr = pIPHead;
while( curr )
{ //search to the end of the list
GetItemPoolFromGround( curr->sGridNo, &gpItemPool );
while( gpItemPool )
{
pObject = &gWorldItems[ gpItemPool->iItemIndex ].object;
if( pObject->usItem == usItem )
{
SpecifyItemToEdit( pObject, gWorldItems[ gpItemPool->iItemIndex ].sGridNo );
CenterScreenAtMapIndex( gsItemGridNo );
return; //success! (found first item in the list)
}
gpItemPool = gpItemPool->pNext;
}
curr = curr->next;
}
}
void SelectNextKeyOfType( UINT8 ubKeyID )
{
IPListNode *curr;
OBJECTTYPE *pObject;
if( gpItemPool )
{
curr = pIPHead;
while( curr )
{ //skip quickly to the same gridno as the item pool
if( curr->sGridNo == gWorldItems[ gpItemPool->iItemIndex ].sGridNo )
{
gpItemPool = gpItemPool->pNext;
while( gpItemPool )
{
pObject = &gWorldItems[ gpItemPool->iItemIndex ].object;
if( Item[ pObject->usItem ].usItemClass == IC_KEY && (*pObject)[0]->data.key.ubKeyID == ubKeyID )
{
SpecifyItemToEdit( pObject, gWorldItems[ gpItemPool->iItemIndex ].sGridNo );
CenterScreenAtMapIndex( gsItemGridNo );
return; //success! (another item in same itempool)
}
gpItemPool = gpItemPool->pNext;
}
curr = curr->next;
break;
}
curr = curr->next;
}
while( curr )
{ //search to the end of the list
GetItemPoolFromGround( curr->sGridNo, &gpItemPool );
while( gpItemPool )
{
pObject = &gWorldItems[ gpItemPool->iItemIndex ].object;
if( Item[ pObject->usItem ].usItemClass == IC_KEY && (*pObject)[0]->data.key.ubKeyID == ubKeyID )
{
SpecifyItemToEdit( pObject, gWorldItems[ gpItemPool->iItemIndex ].sGridNo );
CenterScreenAtMapIndex( gsItemGridNo );
return; //success! (found another item before reaching the end of the list)
}
gpItemPool = gpItemPool->pNext;
}
curr = curr->next;
}
}
curr = pIPHead;
while( curr )
{ //search to the end of the list
GetItemPoolFromGround( curr->sGridNo, &gpItemPool );
while( gpItemPool )
{
pObject = &gWorldItems[ gpItemPool->iItemIndex ].object;
if( Item[ pObject->usItem ].usItemClass == IC_KEY && (*pObject)[0]->data.key.ubKeyID == ubKeyID )
{
SpecifyItemToEdit( pObject, gWorldItems[ gpItemPool->iItemIndex ].sGridNo );
CenterScreenAtMapIndex( gsItemGridNo );
return; //success! (found first item in the list)
}
gpItemPool = gpItemPool->pNext;
}
curr = curr->next;
}
}
void SelectNextTriggerWithFrequency( UINT16 usItem, INT8 bFrequency )
{
IPListNode *curr;
OBJECTTYPE *pObject;
if( gpItemPool )
{
curr = pIPHead;
while( curr )
{ //skip quickly to the same gridno as the item pool
if( curr->sGridNo == gWorldItems[ gpItemPool->iItemIndex ].sGridNo )
{
gpItemPool = gpItemPool->pNext;
while( gpItemPool )
{
pObject = &gWorldItems[ gpItemPool->iItemIndex ].object;
if( pObject->usItem == usItem && (*pObject)[0]->data.misc.bFrequency == bFrequency )
{
SpecifyItemToEdit( pObject, gWorldItems[ gpItemPool->iItemIndex ].sGridNo );
CenterScreenAtMapIndex( gsItemGridNo );
return; //success! (another item in same itempool)
}
gpItemPool = gpItemPool->pNext;
}
curr = curr->next;
break;
}
curr = curr->next;
}
while( curr )
{ //search to the end of the list
GetItemPoolFromGround( curr->sGridNo, &gpItemPool );
while( gpItemPool )
{
pObject = &gWorldItems[ gpItemPool->iItemIndex ].object;
if( pObject->usItem == usItem && (*pObject)[0]->data.misc.bFrequency == bFrequency )
{
SpecifyItemToEdit( pObject, gWorldItems[ gpItemPool->iItemIndex ].sGridNo );
CenterScreenAtMapIndex( gsItemGridNo );
return; //success! (found another item before reaching the end of the list)
}
gpItemPool = gpItemPool->pNext;
}
curr = curr->next;
}
}
curr = pIPHead;
while( curr )
{ //search to the end of the list
GetItemPoolFromGround( curr->sGridNo, &gpItemPool );
while( gpItemPool )
{
pObject = &gWorldItems[ gpItemPool->iItemIndex ].object;
if( pObject->usItem == usItem && (*pObject)[0]->data.misc.bFrequency == bFrequency )
{
SpecifyItemToEdit( pObject, gWorldItems[ gpItemPool->iItemIndex ].sGridNo );
CenterScreenAtMapIndex( gsItemGridNo );
return; //success! (found first item in the list)
}
gpItemPool = gpItemPool->pNext;
}
curr = curr->next;
}
}
void SelectNextPressureAction()
{
IPListNode *curr;
OBJECTTYPE *pObject;
if( gpItemPool )
{
curr = pIPHead;
while( curr )
{ //skip quickly to the same gridno as the item pool
if( curr->sGridNo == gWorldItems[ gpItemPool->iItemIndex ].sGridNo )
{
gpItemPool = gpItemPool->pNext;
while( gpItemPool )
{
pObject = &gWorldItems[ gpItemPool->iItemIndex ].object;
if( pObject->usItem == ACTION_ITEM && (*pObject)[0]->data.misc.bDetonatorType == BOMB_PRESSURE )
{
SpecifyItemToEdit( pObject, gWorldItems[ gpItemPool->iItemIndex ].sGridNo );
CenterScreenAtMapIndex( gsItemGridNo );
return; //success! (another item in same itempool)
}
gpItemPool = gpItemPool->pNext;
}
curr = curr->next;
break;
}
curr = curr->next;
}
while( curr )
{ //search to the end of the list
GetItemPoolFromGround( curr->sGridNo, &gpItemPool );
while( gpItemPool )
{
pObject = &gWorldItems[ gpItemPool->iItemIndex ].object;
if( pObject->usItem == ACTION_ITEM && (*pObject)[0]->data.misc.bDetonatorType == BOMB_PRESSURE )
{
SpecifyItemToEdit( pObject, gWorldItems[ gpItemPool->iItemIndex ].sGridNo );
CenterScreenAtMapIndex( gsItemGridNo );
return; //success! (found another item before reaching the end of the list)
}
gpItemPool = gpItemPool->pNext;
}
curr = curr->next;
}
}
curr = pIPHead;
while( curr )
{ //search to the end of the list
GetItemPoolFromGround( curr->sGridNo, &gpItemPool );
while( gpItemPool )
{
pObject = &gWorldItems[ gpItemPool->iItemIndex ].object;
if( pObject->usItem == ACTION_ITEM && (*pObject)[0]->data.misc.bDetonatorType == BOMB_PRESSURE )
{
SpecifyItemToEdit( pObject, gWorldItems[ gpItemPool->iItemIndex ].sGridNo );
CenterScreenAtMapIndex( gsItemGridNo );
return; //success! (found first item in the list)
}
gpItemPool = gpItemPool->pNext;
}
curr = curr->next;
}
}
UINT16 CountNumberOfItemPlacementsInWorld( UINT16 usItem, UINT16 *pusQuantity )
{
ITEM_POOL *pItemPool;
IPListNode *pIPCurr;
INT16 num = 0;
*pusQuantity = 0;
pIPCurr = pIPHead;
while( pIPCurr )
{
GetItemPoolFromGround( pIPCurr->sGridNo, &pItemPool );
while( pItemPool )
{
if( gWorldItems[ pItemPool->iItemIndex ].object.usItem == usItem )
{
num++;
*pusQuantity += gWorldItems[ pItemPool->iItemIndex ].object.ubNumberOfObjects;
}
pItemPool = pItemPool->pNext;
}
pIPCurr = pIPCurr->next;
}
return num;
}
UINT16 CountNumberOfItemsWithFrequency( UINT16 usItem, INT8 bFrequency )
{
ITEM_POOL *pItemPool;
IPListNode *pIPCurr;
UINT16 num = 0;
pIPCurr = pIPHead;
while( pIPCurr )
{
GetItemPoolFromGround( pIPCurr->sGridNo, &pItemPool );
while( pItemPool )
{
if( gWorldItems[ pItemPool->iItemIndex ].object.usItem == usItem &&
gWorldItems[ pItemPool->iItemIndex ].object[0]->data.misc.bFrequency == bFrequency )
{
num++;
}
pItemPool = pItemPool->pNext;
}
pIPCurr = pIPCurr->next;
}
return num;
}
UINT16 CountNumberOfPressureActionsInWorld()
{
ITEM_POOL *pItemPool;
IPListNode *pIPCurr;
UINT16 num = 0;
pIPCurr = pIPHead;
while( pIPCurr )
{
GetItemPoolFromGround( pIPCurr->sGridNo, &pItemPool );
while( pItemPool )
{
if( gWorldItems[ pItemPool->iItemIndex ].object.usItem == ACTION_ITEM &&
gWorldItems[ pItemPool->iItemIndex ].object[0]->data.misc.bDetonatorType == BOMB_PRESSURE )
{
num++;
}
pItemPool = pItemPool->pNext;
}
pIPCurr = pIPCurr->next;
}
return num;
}
UINT16 CountNumberOfEditorPlacementsInWorld( UINT16 usEInfoIndex, UINT16 *pusQuantity )
{
UINT16 usNumPlacements;
if( eInfo.uiItemType == TBAR_MODE_ITEM_TRIGGERS )
{ //find identical items with same frequency
INT8 bFrequency;
if( usEInfoIndex < PRESSURE_ACTION_ID )
{
if( usEInfoIndex < 2 )
bFrequency = PANIC_FREQUENCY;
else if( usEInfoIndex < 4 )
bFrequency = PANIC_FREQUENCY_2;
else if( usEInfoIndex < 6 )
bFrequency = PANIC_FREQUENCY_3;
else
bFrequency = (INT8)(FIRST_MAP_PLACED_FREQUENCY + (usEInfoIndex-4) / 2);
usNumPlacements = CountNumberOfItemsWithFrequency( eInfo.pusItemIndex[usEInfoIndex], bFrequency );
*pusQuantity = usNumPlacements;
}
else
{
usNumPlacements = CountNumberOfPressureActionsInWorld();
*pusQuantity = usNumPlacements;
}
}
else if( eInfo.uiItemType == TBAR_MODE_ITEM_KEYS )
{
usNumPlacements = CountNumberOfKeysOfTypeInWorld( (UINT8)usEInfoIndex );
*pusQuantity = usNumPlacements;
}
else
{
usNumPlacements = CountNumberOfItemPlacementsInWorld( eInfo.pusItemIndex[ usEInfoIndex], pusQuantity );
}
return usNumPlacements;
}
UINT16 CountNumberOfKeysOfTypeInWorld( UINT8 ubKeyID )
{
ITEM_POOL *pItemPool;
IPListNode *pIPCurr;
INT16 num = 0;
pIPCurr = pIPHead;
while( pIPCurr )
{
GetItemPoolFromGround( pIPCurr->sGridNo, &pItemPool );
while( pItemPool )
{
if( Item[ gWorldItems[ pItemPool->iItemIndex ].object.usItem ].usItemClass == IC_KEY )
{
if( gWorldItems[ pItemPool->iItemIndex ].object[0]->data.key.ubKeyID == ubKeyID )
{
num++;
}
}
pItemPool = pItemPool->pNext;
}
pIPCurr = pIPCurr->next;
}
return num;
}
void DisplayItemStatistics()
{
BOOLEAN fUseSelectedItem;
INT16 usItemIndex;
CHAR16 pItemName[SIZE_ITEM_NAME];
INVTYPE *pItem;
if( !eInfo.fActive )
{
return;
}
//If there is nothing else currently highlited by the mouse, use the selected item.
fUseSelectedItem = eInfo.sHilitedItemIndex == -1 || eInfo.sHilitedItemIndex == eInfo.sSelItemIndex;
SetFont( SMALLCOMPFONT );
SetFontForeground( (UINT8)(fUseSelectedItem ? FONT_LTRED : FONT_YELLOW) );
//Extract all of the item information.
if( !eInfo.pusItemIndex )
return;
usItemIndex = eInfo.pusItemIndex[ fUseSelectedItem ? eInfo.sSelItemIndex : eInfo.sHilitedItemIndex ];
pItem = &Item[ usItemIndex ];
LoadItemInfo( usItemIndex, pItemName, NULL );
mprintf( iScreenWidthOffset + 50 - StringPixLength( pItemName , SMALLCOMPFONT )/2, 2 * iScreenHeightOffset + 403, pItemName );
mprintf( iScreenWidthOffset + 2, 2 * iScreenHeightOffset + 410, L"Status Info Line 1");
mprintf( iScreenWidthOffset + 2, 2 * iScreenHeightOffset + 420, L"Status Info Line 2");
mprintf( iScreenWidthOffset + 2, 2 * iScreenHeightOffset + 430, L"Status Info Line 3");
mprintf( iScreenWidthOffset + 2, 2 * iScreenHeightOffset + 440, L"Status Info Line 4");
mprintf( iScreenWidthOffset + 2, 2 * iScreenHeightOffset + 450, L"Status Info Line 5");
}
#endif