New feature: interactive actions allow various actions (hacking/reading/drinking/...) from user-defined xml acttions. The exact results can be set in a lua function.

For more info, see http://thepit.ja-galaxy-forum.com/index.php?t=msg&th=23144&goto=346447&#msg_346447

This is fully savegame compatible.

GameDir >= r2333 is required.

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@8278 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
Flugente
2016-08-02 19:03:28 +00:00
parent 41dcb598dd
commit 99f5be0ed7
48 changed files with 1406 additions and 60 deletions
+6 -1
View File
@@ -2212,7 +2212,12 @@ void HandleRenderFaceAdjustments( FACETYPE *pFace, BOOLEAN fDisplayBuffer, BOOLE
}
// Flugente: add an icon if we are performing a multi-turn action
if ( MercPtrs[ pFace->ubSoldierID ]->GetMultiTurnAction() > MTA_NONE )
if ( MercPtrs[pFace->ubSoldierID]->GetMultiTurnAction( ) == MTA_HACK )
{
DoRightIcon( uiRenderBuffer, pFace, sFaceX, sFaceY, bNumRightIcons, 30 );
bNumRightIcons++;
}
else if ( MercPtrs[ pFace->ubSoldierID ]->GetMultiTurnAction() > MTA_NONE )
{
DoRightIcon( uiRenderBuffer, pFace, sFaceX, sFaceY, bNumRightIcons, 14 );
bNumRightIcons++;
+35
View File
@@ -1118,4 +1118,39 @@ BOOLEAN HasFoodInInventory( SOLDIERTYPE *pSoldier, BOOLEAN fCheckFood, BOOLEAN f
}
return FALSE;
}
void DrinkFromWaterTap( SOLDIERTYPE* pSoldier )
{
if ( !pSoldier )
return;
// dont feed our machines
if ( pSoldier->ubProfile == ROBOT || IsVehicle( pSoldier ) )
return;
if ( gGameOptions.fFoodSystem )
{
INT32 watertoadd = max( 0, FoodMoraleMods[FOOD_NORMAL].bThreshold - pSoldier->bDrinkLevel );
if ( watertoadd > 0 )
{
AddFoodpoints( pSoldier->bDrinkLevel, watertoadd );
}
}
if ( GetWaterQuality( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ) == WATER_POISONOUS )
HandlePossibleInfection( pSoldier, NULL, INFECTION_TYPE_BADWATER );
INT32 bpadded = 100 * min( 20, 100 - pSoldier->bBreath );
DeductPoints( pSoldier, 20, -bpadded );
// play water sound
if ( pSoldier->bTeam == gbPlayerNum && gGameExternalOptions.fFoodEatingSounds )
{
PlayJA2SampleFromFile( "Sounds\\watertap.wav", RATE_11025, SoundVolume( MIDVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) );
}
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szInteractiveActionText[8], pSoldier->GetName( ) );
}
+2
View File
@@ -107,4 +107,6 @@ void SoldierAutoFillCanteens(SOLDIERTYPE *pSoldier);
BOOLEAN HasFoodInInventory( SOLDIERTYPE *pSoldier, BOOLEAN fCheckFood, BOOLEAN fCheckDrink );
void DrinkFromWaterTap( SOLDIERTYPE *pSoldier );
#endif
+246 -11
View File
@@ -72,6 +72,10 @@
#include <fstream> // added by Flugente
#include "DisplayCover.h" // added by Flugente
#include "Queen Command.h" // added by Flugente for FindUnderGroundSector(...)
#include "LuaInitNPCs.h" // added by Flugente
#include "finances.h" // added by Flugente
#include "LaptopSave.h" // added by Flugente
#include "Game Clock.h" // added by Flugente
#include <vfs/Core/vfs_file_raii.h> // added by Flugente for vfs-stuff
#endif
@@ -246,11 +250,11 @@ INT32 HandleItem( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel, UINT16 usHa
// "attached weapon"
pSoldier->usAttackingWeapon = usHandItem;
// sevenfm: set shift flag for auto-taking of next item from inventory
if( fFromUI && _KeyDown(SHIFT) )
gfShiftBombPlant = TRUE;
else
gfShiftBombPlant = FALSE;
// sevenfm: set shift flag for auto-taking of next item from inventory
if( fFromUI && _KeyDown(SHIFT) )
gfShiftBombPlant = TRUE;
else
gfShiftBombPlant = FALSE;
// Find soldier flags depend on if it's our own merc firing or a NPC
//if ( FindSoldier( sGridNo, &usSoldierIndex, &uiMercFlags, FIND_SOLDIER_GRIDNO ) )
@@ -325,7 +329,90 @@ INT32 HandleItem( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel, UINT16 usHa
return( ITEM_HANDLE_REFUSAL );
}
}
}
INT32 usMapPos;
GetMouseMapPos( &usMapPos );
// Flugente: interactive actions
if ( Item[usHandItem].ubCursor == INVALIDCURS || usHandItem == NONE )
{
UINT16 structindex;
UINT16 possibleaction = InteractiveActionPossibleAtGridNo( usMapPos, pSoldier->pathing.bLevel, structindex );
if ( possibleaction )//&& pSoldier->GetInteractiveActionSkill( usMapPos, pSoldier->pathing.bLevel, possibleaction ) )
{
// ATE: AI CANNOT GO THROUGH HERE!
BOOLEAN fHadToUseCursorPos = FALSE;
// See if we can get there to stab
sActionGridNo = FindAdjacentGridEx( pSoldier, usMapPos, &ubDirection, &sAdjustedGridNo, TRUE, FALSE );
if ( sActionGridNo == NOWHERE )
{
// Try another location...
sActionGridNo = FindAdjacentGridEx( pSoldier, sGridNo, &ubDirection, &sAdjustedGridNo, TRUE, FALSE );
if ( sActionGridNo == NOWHERE )
{
return(ITEM_HANDLE_CANNOT_GETTO_LOCATION);
}
}
// Calculate AP costs...
sAPCost = 0;//GetAPsToHandcuff( pSoldier, sActionGridNo );
sAPCost += PlotPath( pSoldier, sActionGridNo, NO_COPYROUTE, FALSE, TEMPORARY, (UINT16)pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints );
if ( EnoughPoints( pSoldier, sAPCost, 0, fFromUI ) )
{
// OK, set UI
SetUIBusy( pSoldier->ubID );
// CHECK IF WE ARE AT THIS GRIDNO NOW
if ( pSoldier->sGridNo != sActionGridNo )
{
// SEND PENDING ACTION
pSoldier->aiData.ubPendingAction = MERC_INTERACTIVEACTION;
if ( fHadToUseCursorPos )
{
pSoldier->aiData.sPendingActionData2 = usMapPos;
}
else
{
if ( pTargetSoldier != NULL )
{
pSoldier->aiData.sPendingActionData2 = pTargetSoldier->sGridNo;
}
else
{
pSoldier->aiData.sPendingActionData2 = sGridNo;
}
}
pSoldier->aiData.bPendingActionData3 = (INT8)(possibleaction);
pSoldier->aiData.ubPendingActionAnimCount = 0;
// WALK UP TO DEST FIRST
pSoldier->EVENT_InternalGetNewSoldierPath( sActionGridNo, pSoldier->usUIMovementMode, FALSE, TRUE );
}
else
{
pSoldier->EVENT_SoldierInteractiveAction( sAdjustedGridNo, possibleaction );
UnSetUIBusy( pSoldier->ubID );
}
if ( fFromUI )
{
guiPendingOverrideEvent = A_CHANGE_TO_MOVE;
}
return(ITEM_HANDLE_OK);
}
else
{
return(ITEM_HANDLE_NOAPS);
}
}
}
// Check HAND ITEM
@@ -1236,11 +1323,8 @@ INT32 HandleItem( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel, UINT16 usHa
if ( gGameExternalOptions.fAllowPrisonerSystem && HasItemFlag( usHandItem, HANDCUFFS ) && pTargetSoldier && pTargetSoldier->CanBeCaptured( ) )
{
// ATE: AI CANNOT GO THROUGH HERE!
INT32 usMapPos;
BOOLEAN fHadToUseCursorPos = FALSE;
GetMouseMapPos( &usMapPos );
// See if we can get there to stab
sActionGridNo = FindAdjacentGridEx( pSoldier, sGridNo, &ubDirection, &sAdjustedGridNo, TRUE, FALSE );
if ( sActionGridNo == -1 )
@@ -1319,11 +1403,8 @@ INT32 HandleItem( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel, UINT16 usHa
if ( ItemCanBeAppliedToOthers( usHandItem ) )
{
// ATE: AI CANNOT GO THROUGH HERE!
INT32 usMapPos;
BOOLEAN fHadToUseCursorPos = FALSE;
GetMouseMapPos( &usMapPos );
// See if we can get there to stab
sActionGridNo = FindAdjacentGridEx( pSoldier, sGridNo, &ubDirection, &sAdjustedGridNo, TRUE, FALSE );
if ( sActionGridNo == -1 )
@@ -8696,3 +8777,157 @@ void HandleTakeNewBombFromInventory(SOLDIERTYPE* pSoldier, OBJECTTYPE* pObj)
pSoldier->TakeNewBombFromInventory(pObj->usItem);
}
}
// Flugente: interactive actions
void DoInteractiveAction( INT32 sGridNo, SOLDIERTYPE *pSoldier )
{
// we need a valid soldier and a valid object
if ( !pSoldier )
return;
// needs to be a valid location
if ( TileIsOutOfBounds( sGridNo ) )
return;
UINT16 structindex;
UINT16 possibleaction = InteractiveActionPossibleAtGridNo( sGridNo, pSoldier->pathing.bLevel, structindex );
UINT16 skill = pSoldier->GetInteractiveActionSkill( sGridNo, pSoldier->pathing.bLevel, possibleaction );
INT32 difficulty = gInteractiveStructure[structindex].difficulty;
INT32 luaactionid = gInteractiveStructure[structindex].luaactionid;
// play sounds
switch ( possibleaction )
{
case INTERACTIVE_STRUCTURE_HACKABLE:
PlayJA2SampleFromFile( "Sounds\\keyboard_typing.wav", RATE_11025, SoundVolume( MIDVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) );
break;
case INTERACTIVE_STRUCTURE_READFILE:
PlayJA2SampleFromFile( "Sounds\\book_pageturn1.wav", RATE_11025, SoundVolume( MIDVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) );
break;
default:
break;
}
if ( possibleaction == INTERACTIVE_STRUCTURE_HACKABLE )
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szInteractiveActionText[0], pSoldier->GetName( ) );
// hacking is a multiturn-action - we only start the action here, the result is handled elsewhere
pSoldier->StartMultiTurnAction( MTA_HACK, sGridNo );
}
// call lua with the action id - perhaps we might do something special here
else if ( luaactionid >= 0 )
{
LuaHandleInteractiveActionResult( gWorldSectorX, gWorldSectorY, gbWorldSectorZ, sGridNo, pSoldier->pathing.bLevel, pSoldier->ubID, possibleaction, luaactionid, difficulty, skill );
}
else
{
DoInteractiveActionDefaultResult( sGridNo, pSoldier->ubID, (skill >= difficulty) );
}
}
// handle the default result of an interactive action
// This is called either if no lua action id is set, or by lua if this should happen as a supplement to whatever lua does
void DoInteractiveActionDefaultResult( INT32 sGridNo, UINT8 ubID, BOOLEAN aSuccess )
{
SOLDIERTYPE* pSoldier = NULL;
if ( ubID != NOBODY )
pSoldier = MercPtrs[ubID];
// we need a valid soldier and a valid object
if ( !pSoldier )
return;
// needs to be a valid location
if ( TileIsOutOfBounds( sGridNo ) )
return;
UINT16 structindex;
UINT16 possibleaction = InteractiveActionPossibleAtGridNo( sGridNo, pSoldier->pathing.bLevel, structindex );
switch ( possibleaction )
{
case INTERACTIVE_STRUCTURE_HACKABLE:
{
if ( aSuccess )
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szInteractiveActionText[1], pSoldier->GetName( ) );
}
else
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szInteractiveActionText[2], pSoldier->GetName( ) );
}
}
break;
case INTERACTIVE_STRUCTURE_READFILE:
{
if ( aSuccess )
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szInteractiveActionText[3], pSoldier->GetName( ) );
}
else
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szInteractiveActionText[4], pSoldier->GetName( ) );
}
}
break;
case INTERACTIVE_STRUCTURE_WATERTAP:
{
if ( aSuccess )
{
DrinkFromWaterTap( pSoldier );
}
else
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szInteractiveActionText[5], pSoldier->GetName( ) );
}
}
break;
case INTERACTIVE_STRUCTURE_SODAMACHINE:
{
static UINT16 usSodaIndex = 1741;
// we still have to determine whether a soda item exists, and whether we can pay for it
if ( aSuccess )
{
aSuccess = FALSE;
// determine soda item, only if this exists can we proceed to sell one
if ( HasItemFlag( usSodaIndex, SODA ) || GetFirstItemWithFlag( &usSodaIndex, SODA ) )
{
if ( LaptopSaveInfo.iCurrentBalance >= Item[usSodaIndex].usPrice )
{
aSuccess = TRUE;
}
}
}
if ( aSuccess )
{
if ( CreateItem( usSodaIndex, 100, &gTempObject ) && AutoPlaceObjectAnywhere( pSoldier, &gTempObject, TRUE ) )
{
AddTransactionToPlayersBook( TRANSFER_FUNDS_TO_MERC, pSoldier->ubProfile, GetWorldTotalMin( ), Item[usSodaIndex].usPrice );
PlayJA2SampleFromFile( "Sounds\\soda_machine.wav", RATE_11025, SoundVolume( MIDVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) );
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szInteractiveActionText[6], pSoldier->GetName( ), Item[usSodaIndex].szItemName );
}
}
else
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, szInteractiveActionText[7], pSoldier->GetName( ) );
}
}
break;
default:
break;
}
}
+50
View File
@@ -111,6 +111,52 @@ typedef struct {
extern STRUCTURE_CONSTRUCT gStructureConstruct[STRUCTURE_CONSTRUCT_MAX];
// Flugente: we can define what structures we can interact with (example: we can hack computers or retrieve information from a file cabinet)
typedef enum
{
INTERACTIVE_STRUCTURE_NO_ACTION,
INTERACTIVE_STRUCTURE_HACKABLE,
INTERACTIVE_STRUCTURE_READFILE,
INTERACTIVE_STRUCTURE_WATERTAP,
INTERACTIVE_STRUCTURE_SODAMACHINE,
INTERACTIVE_STRUCTURE_TYPE_MAX,
} INTERACTIVE_STRUCTURE_TYPE;
typedef struct INTERACTIVE_STRUCTURE {
void reset()
{
sector = -1;
sectorlevel = -1;
tileindexvector.clear( );
gridnovector.clear( );
sLevel = -1;
sActionType = INTERACTIVE_STRUCTURE_NO_ACTION;
difficulty = 0;
luaactionid = -1;
}
INT16 sector;
INT8 sectorlevel; // whether this is on the surface (0) or below (1-3)
char szTileSetName[20]; // name of the tileset
std::vector<UINT16> tileindexvector; // possible indizes in tileset
// We can either specify a specific location, or simply leave this empty
// in that case, this action will apply to all locations fitting the above criteria, but a specific location definition always takes preference
std::vector<INT32> gridnovector; // possible gridnos
INT8 sLevel;
UINT16 sActionType; // one of INTERACTIVE_STRUCTURE_TYPE
INT32 difficulty;
INT32 luaactionid; // call lua with this ID after performing the usual action - perhaps something special has to happen
} INTERACTIVE_STRUCTURE;
#define INTERACTIVE_STRUCTURE_MAX 200
extern INTERACTIVE_STRUCTURE gInteractiveStructure[INTERACTIVE_STRUCTURE_MAX];
extern UINT32 gMaxInteractiveStructureRead;
class SOLDIERTYPE;
INT32 HandleItem( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel, UINT16 usHandItem, BOOLEAN fFromUI );
void SoldierPickupItem( SOLDIERTYPE *pSoldier, INT32 iItemIndex, INT32 sGridNo, INT8 bZLevel );
@@ -253,4 +299,8 @@ INT32 GetFirstObjectInSectorPosition( UINT16 ausItem );
extern ITEM_POOL *gpItemPool;//dnl ch26 210909
// Flugente: interactive actions
void DoInteractiveAction( INT32 sGridNo, SOLDIERTYPE *pSoldier );
void DoInteractiveActionDefaultResult( INT32 sGridNo, UINT8 ubID, BOOLEAN aSuccess );
#endif
+39 -4
View File
@@ -4258,6 +4258,26 @@ INT8 DrawUIMovementPath( SOLDIERTYPE *pSoldier, INT32 usMapPos, UINT32 uiFlags )
gsUIHandleShowMoveGridLocation = sActionGridNo;
}
}
else if ( uiFlags == MOVEUI_TARGET_INTERACTIVEACTION )
{
sActionGridNo = FindAdjacentGridEx( pSoldier, usMapPos, &ubDirection, NULL, FALSE, TRUE );
if ( sActionGridNo == -1 )
{
sActionGridNo = usMapPos;
}
UINT16 structindex;
UINT16 possibleaction = InteractiveActionPossibleAtGridNo( usMapPos, pSoldier->pathing.bLevel, structindex );
sAPCost = GetAPsForInteractiveAction( pSoldier, possibleaction );
sAPCost += UIPlotPath( pSoldier, sActionGridNo, NO_COPYROUTE, fPlot, TEMPORARY, (UINT16)pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints );
if ( sActionGridNo != pSoldier->sGridNo )
{
gfUIHandleShowMoveGrid = TRUE;
gsUIHandleShowMoveGridLocation = sActionGridNo;
}
}
else if ( uiFlags == MOVEUI_TARGET_MERCS )
{
INT32 sGotLocation = NOWHERE;
@@ -4600,6 +4620,23 @@ BOOLEAN UIMouseOnValidAttackLocation( SOLDIERTYPE *pSoldier )
return( FALSE );
}
if ( ubItemCursor == INTERACTIVEACTIONCURS )
{
if ( gfUIFullTargetFound )
{
usMapPos = MercPtrs[gusUIFullTargetID]->sGridNo;
}
UINT16 structindex;
UINT16 possibleaction = InteractiveActionPossibleAtGridNo( usMapPos, pSoldier->pathing.bLevel, structindex );
if ( possibleaction )//&& pSoldier->GetInteractiveActionSkill( usMapPos, pSoldier->pathing.bLevel, possibleaction ) )
{
return TRUE;
}
return FALSE;
}
if ( ubItemCursor == HANDCUFFCURS )
{
if ( HasItemFlag( (&(pSoldier->inv[HANDPOS]))->usItem, HANDCUFFS ) )
@@ -6435,8 +6472,7 @@ INT8 UIHandleInteractiveTilesAndItemsOnTerrain( SOLDIERTYPE *pSoldier, INT32 usM
STRUCTURE *pStructure = NULL;
BOOLEAN fPoolContainsHiddenItems = FALSE;
SOLDIERTYPE *pTSoldier;
if ( gfResetUIItemCursorOptimization )
{
gfResetUIItemCursorOptimization = FALSE;
@@ -6677,9 +6713,8 @@ INT8 UIHandleInteractiveTilesAndItemsOnTerrain( SOLDIERTYPE *pSoldier, INT32 usM
}
}
}
}
if ( pIntTile == NULL )
{
return( 0 );
+1
View File
@@ -32,6 +32,7 @@
#define MOVEUI_TARGET_FORTIFICATION 13
#define MOVEUI_TARGET_HANDCUFF 14
#define MOVEUI_TARGET_APPLYITEM 15
#define MOVEUI_TARGET_INTERACTIVEACTION 16
#define MOVEUI_RETURN_ON_TARGET_MERC 1
+12
View File
@@ -218,6 +218,18 @@ UICursor gUICursors[ NUM_UI_CURSORS ] =
APPLYITEM_GREY_UICURSOR, UICURSOR_FREEFLOWING, CURSOR_APPLYITEM, 0,
APPLYITEM_RED_UICURSOR, UICURSOR_FREEFLOWING, CURSOR_APPLYITEM_RED, 0,
HACK_GREY_UICURSOR, UICURSOR_FREEFLOWING, CURSOR_HACK, 0,
HACK_RED_UICURSOR, UICURSOR_FREEFLOWING, CURSOR_HACK_RED, 0,
READFILE_GREY_UICURSOR, UICURSOR_FREEFLOWING, CURSOR_READFILE, 0,
READFILE_RED_UICURSOR, UICURSOR_FREEFLOWING, CURSOR_READFILE_RED, 0,
WATERTAP_GREY_UICURSOR, UICURSOR_FREEFLOWING, CURSOR_WATERTAP, 0,
WATERTAP_RED_UICURSOR, UICURSOR_FREEFLOWING, CURSOR_WATERTAP_RED, 0,
SODAMACHINE_GREY_UICURSOR, UICURSOR_FREEFLOWING, CURSOR_SODAMACHINE, 0,
SODAMACHINE_RED_UICURSOR, UICURSOR_FREEFLOWING, CURSOR_SODAMACHINE_RED, 0,
};
+12
View File
@@ -195,6 +195,18 @@ typedef enum
APPLYITEM_GREY_UICURSOR,
APPLYITEM_RED_UICURSOR,
HACK_GREY_UICURSOR,
HACK_RED_UICURSOR,
READFILE_GREY_UICURSOR,
READFILE_RED_UICURSOR,
WATERTAP_GREY_UICURSOR,
WATERTAP_RED_UICURSOR,
SODAMACHINE_GREY_UICURSOR,
SODAMACHINE_RED_UICURSOR,
NUM_UI_CURSORS
} UICursorDefines;
+2 -1
View File
@@ -197,7 +197,8 @@ enum {
BG_DISLIKEBG, // dislike any other background that has the negative of this value set
BG_SMOKERTYPE, // 0: doesnt care about smoking 1: will consume cigarettes, dislikes non-smokers 2: will refuse to smoke, dislikes smokers
BG_CROUCHEDDEFENSE, // lowers enemy cth if they fire at us while we are crouched against cover in the direction the shots come from
BG_FORTIFY_ASSIGNMENT, // modifies effectivity of 'FORTIFICATION' assignemnt
BG_FORTIFY_ASSIGNMENT, // modifies effectivity of 'FORTIFICATION' assignment
BG_HACKERSKILL, // hacking skill from 0 to 100, > 0 means hacking is possible
BG_MAX,
};
+5 -1
View File
@@ -176,6 +176,7 @@ typedef enum ATTACHMENT_SLOT{
#define FORTICURS 24
#define HANDCUFFCURS 25
#define APPLYITEMCURS 26
#define INTERACTIVEACTIONCURS 27
#define CAMERARANGE 10
@@ -763,7 +764,7 @@ extern OBJECTTYPE gTempObject;
#define TRIPWIREROLL 0x01000000 //16777216 // this item is a tripwire roll
#define RADIO_SET 0x02000000 //33554432 // item can be used to radio militia/squads in other sectors
#define SIGNAL_SHELL 0x04000000 //67108864 // this is a signal shell that precedes artillery barrages
//#define POWER_PACK 0x08000000 //134217728 // item continously powers an item it is attached to
#define SODA 0x08000000 //134217728 // item is a can of soda, sold in vending machines
#define ROOF_COLLAPSE_ITEM 0x10000000 //268435456 // this item is required in the collapsing of roof tiles. It is used internally and should never be seen by the player
#define DISEASEPROTECTION_1 0x20000000 //536870912 // this item protects us from getting diseases by human contact if kept in inventory
@@ -1138,6 +1139,9 @@ typedef struct
UINT8 DisarmModifier;
INT8 RepairModifier;
// Flugente: a modifier to hacking
UINT8 usHackingModifier;
// Flugente: advanced repair/dirt system
UINT8 usDamageChance; // chance that damage to the status will also damage the repair threshold
FLOAT dirtIncreaseFactor; // one shot causes this much dirt on a gun
+10 -2
View File
@@ -1382,6 +1382,10 @@ BOOLEAN ExecuteOverhead( )
}
}
}
// Flugente: we are doing something here - stop multi-turn-action
pSoldier->CancelMultiTurnAction( FALSE );
if ( pSoldier->aiData.ubPendingAction == MERC_PICKUPITEM )
{
sGridNo = pSoldier->aiData.sPendingActionData2;
@@ -1538,6 +1542,11 @@ BOOLEAN ExecuteOverhead( )
pSoldier->EVENT_SoldierBeginGiveItem( );
pSoldier->aiData.ubPendingAction = NO_PENDING_ACTION;
}
else if ( pSoldier->aiData.ubPendingAction == MERC_INTERACTIVEACTION )
{
pSoldier->EVENT_SoldierInteractiveAction( pSoldier->aiData.sPendingActionData2, pSoldier->aiData.bPendingActionData3 );
pSoldier->aiData.ubPendingAction = NO_PENDING_ACTION;
}
if ( fNoAPsForPendingAction )
{
@@ -1547,8 +1556,7 @@ BOOLEAN ExecuteOverhead( )
pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
pSoldier->ubPendingDirection = NO_PENDING_DIRECTION;
}
}
}
else
{
// OK, ADJUST TO STANDING, WE ARE DONE
+29
View File
@@ -4000,6 +4000,9 @@ INT16 GetAPsForMultiTurnAction( SOLDIERTYPE *pSoldier, UINT8 usActionType )
case MTA_REMOVE_FORTIFY:
sAPCost += APBPConstants[AP_REMOVE_FORTIFICATION];
break;
case MTA_HACK:
sAPCost += APBPConstants[AP_HACK];
break;
}
if ( usActionType == MTA_FORTIFY || usActionType == MTA_REMOVE_FORTIFY )
@@ -4010,6 +4013,32 @@ INT16 GetAPsForMultiTurnAction( SOLDIERTYPE *pSoldier, UINT8 usActionType )
return sAPCost;
}
INT16 GetAPsForInteractiveAction( SOLDIERTYPE *pSoldier, UINT8 usActionType )
{
INT16 sAPCost = 0;
switch ( usActionType )
{
case INTERACTIVE_STRUCTURE_HACKABLE:
sAPCost = GetAPsForMultiTurnAction( pSoldier, MTA_HACK );
break;
case INTERACTIVE_STRUCTURE_READFILE:
sAPCost += APBPConstants[AP_READFILE];
break;
case INTERACTIVE_STRUCTURE_WATERTAP:
sAPCost += APBPConstants[AP_WATERTAP];
break;
case INTERACTIVE_STRUCTURE_SODAMACHINE:
sAPCost += APBPConstants[AP_SODAMACHINE];
break;
default:
break;
}
return sAPCost;
}
INT16 GetAPsToJumpOver( SOLDIERTYPE *pSoldier )
{
// -25% APs needed to for MA traits
+2
View File
@@ -356,6 +356,8 @@ INT16 GetAPsToHandcuff( SOLDIERTYPE *pSoldier, INT32 usMapPos ); // added by Flu
INT16 GetAPsToApplyItem( SOLDIERTYPE *pSoldier, INT32 usMapPos ); // added by Flugente
INT16 GetAPsForMultiTurnAction( SOLDIERTYPE *pSoldier, UINT8 usActionType ); // added by Flugente
INT16 GetAPsForInteractiveAction( SOLDIERTYPE *pSoldier, UINT8 usActionType ); // added by Flugente
INT16 GetAPsToJumpOver( SOLDIERTYPE *pSoldier );
void GetAPChargeForShootOrStabWRTGunRaises( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubAddTurningCost, BOOLEAN *pfChargeTurning, BOOLEAN *pfChargeRaise, INT16 bAimTime );
+157 -10
View File
@@ -100,6 +100,7 @@
#include "Cheats.h" // added by Flugente
#include "MilitiaIndividual.h" // added by Flugente
#include "Arms Dealer Init.h" // added by Flugente for armsDealerInfo[]
#include "LuaInitNPCs.h" // added by Flugente
#endif
#include "ub_config.h"
@@ -16262,12 +16263,13 @@ BOOLEAN SOLDIERTYPE::UpdateMultiTurnAction( )
{
case MTA_FORTIFY:
case MTA_REMOVE_FORTIFY:
// building on a roof is forbidden! stop this!
/*// building on a roof is forbidden! stop this!
if ( this->pathing.bLevel != 0 )
{
CancelMultiTurnAction( FALSE );
return FALSE;
}
}*/
// we have to be crouched, get out of here, set us to be crouched first
if ( gAnimControl[this->usAnimState].ubEndHeight != ANIM_CROUCH )
@@ -16275,11 +16277,15 @@ BOOLEAN SOLDIERTYPE::UpdateMultiTurnAction( )
return TRUE;
}
break;
default: // default: exit
{
CancelMultiTurnAction( FALSE );
return FALSE;
}
case MTA_HACK:
break;
default: // default: exit
{
CancelMultiTurnAction( FALSE );
return FALSE;
}
break;
}
@@ -16289,12 +16295,17 @@ BOOLEAN SOLDIERTYPE::UpdateMultiTurnAction( )
// determine the gridno before us and the item we have in our main hand, this is enough for the current actions
OBJECTTYPE* pObj = &(this->inv[HANDPOS]);
// error if the gridno we started working on is not the gridno we are currently looking at
// error if object is missing
if ( usMultiTurnAction == MTA_FORTIFY || usMultiTurnAction == MTA_REMOVE_FORTIFY )
{
if ( !pObj || !(pObj->exists( )) )
fActionStillValid = FALSE;
else if ( this->sMTActionGridNo != NewGridNo( this->sGridNo, DirectionInc( this->ubDirection ) ) )
}
// error if the gridno we started working on is not the gridno we are currently looking at
if ( usMultiTurnAction == MTA_FORTIFY || usMultiTurnAction == MTA_REMOVE_FORTIFY )//|| usMultiTurnAction == MTA_HACK )
{
if ( this->sMTActionGridNo != NewGridNo( this->sGridNo, DirectionInc( this->ubDirection ) ) )
fActionStillValid = FALSE;
}
@@ -16320,7 +16331,7 @@ BOOLEAN SOLDIERTYPE::UpdateMultiTurnAction( )
}
break;
case MTA_REMOVE_FORTIFY:
case MTA_REMOVE_FORTIFY:
{
entireapcost = GetAPsForMultiTurnAction( this, MTA_REMOVE_FORTIFY );
entirebpcost = APBPConstants[BP_REMOVE_FORTIFICATION];
@@ -16329,6 +16340,18 @@ BOOLEAN SOLDIERTYPE::UpdateMultiTurnAction( )
fActionStillValid = FALSE;
}
break;
case MTA_HACK:
{
entireapcost = GetAPsForMultiTurnAction( this, MTA_HACK );
entirebpcost = 0; // hacking isn't exactly hard to do physically :-)
UINT16 structindex;
if ( !this->GetInteractiveActionSkill( this->sMTActionGridNo, this->pathing.bLevel, INTERACTIVE_STRUCTURE_HACKABLE ) ||
!InteractiveActionPossibleAtGridNo( this->sMTActionGridNo, this->pathing.bLevel, structindex ) == INTERACTIVE_STRUCTURE_HACKABLE )
fActionStillValid = FALSE;
}
break;
}
if ( !fActionStillValid )
@@ -16359,6 +16382,14 @@ BOOLEAN SOLDIERTYPE::UpdateMultiTurnAction( )
}
}
break;
case MTA_HACK:
{
// if we are not in turnbased and no enemies are around, we reduce the number of necessary action points to 0. No need to keep waiting if there's nobody around anyway
if ( !(gTacticalStatus.uiFlags & TURNBASED && gTacticalStatus.uiFlags & INCOMBAT) )
bOverTurnAPS = 0;
}
break;
}
// if we can afford it, do it now
@@ -16388,6 +16419,31 @@ BOOLEAN SOLDIERTYPE::UpdateMultiTurnAction( )
}
}
break;
case MTA_HACK:
{
UINT16 structindex;
UINT16 possibleaction = InteractiveActionPossibleAtGridNo( this->sMTActionGridNo, this->pathing.bLevel, structindex );
UINT16 skill = this->GetInteractiveActionSkill( sGridNo, this->pathing.bLevel, possibleaction );
INT32 difficulty = gInteractiveStructure[structindex].difficulty;
INT32 luaactionid = gInteractiveStructure[structindex].luaactionid;
BOOLEAN success = (skill >= difficulty);
if ( possibleaction != INTERACTIVE_STRUCTURE_HACKABLE )
success = FALSE;
// call lua with the action id - perhaps we might do something special here
if ( luaactionid >= 0 )
{
LuaHandleInteractiveActionResult( gWorldSectorX, gWorldSectorY, gbWorldSectorZ, sGridNo, this->pathing.bLevel, this->ubID, possibleaction, luaactionid, difficulty, skill );
}
else
{
DoInteractiveActionDefaultResult( sGridNo, this->ubID, (skill > difficulty) );
}
}
break;
}
if ( entireapcost > 0 )
@@ -19174,6 +19230,92 @@ UINT8 SOLDIERTYPE::GetWaterSnakeDefenseChance()
return (UINT8)(val);
}
// Flugente: interactive actions
UINT16 SOLDIERTYPE::GetInteractiveActionSkill( INT32 sGridNo, UINT8 usLevel, UINT16 usType )
{
switch ( usType )
{
case INTERACTIVE_STRUCTURE_HACKABLE:
{
if ( this->ubProfile == ROBOT || IsVehicle( this ) )
return 0;
UINT16 skill = this->GetBackgroundValue( BG_HACKERSKILL );
// without the background property, we cannot hack at all
if ( !skill )
return 0;
FLOAT bestmodifier = 1.0f;
UINT8 bestequipmentbonus = 0;
OBJECTTYPE* pObj = NULL;
INT8 invsize = (INT8)inv.size( ); // remember inventorysize, so we don't call size() repeatedly
for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop ) // ... for all items in our inventory ...
{
// ... if Item exists and is canteen (that can have drink points) ...
if ( inv[bLoop].exists( ) == true && Item[inv[bLoop].usItem].usHackingModifier )
{
OBJECTTYPE * pObj = &(this->inv[bLoop]); // ... get pointer for this item ...
if ( pObj != NULL ) // ... if pointer is not obviously useless ...
{
for ( INT16 i = 0; i < pObj->ubNumberOfObjects; ++i )
{
FLOAT modifier = 1.0f + (Item[inv[bLoop].usItem].usHackingModifier * (*pObj)[i]->data.objectStatus) / 10000.0f;
if ( modifier > bestmodifier )
bestmodifier = modifier;
}
}
}
}
return (UINT16)(skill * bestmodifier);
}
break;
case INTERACTIVE_STRUCTURE_READFILE:
{
if ( this->ubProfile == ROBOT || IsVehicle( this ) )
return 0;
// reading is governed by wisdom
return this->stats.bWisdom;
}
break;
case INTERACTIVE_STRUCTURE_WATERTAP:
{
if ( this->ubProfile == ROBOT || IsVehicle( this ) )
return 0;
// we are pros at drinking water
return 100;
}
break;
case INTERACTIVE_STRUCTURE_SODAMACHINE:
{
if ( this->ubProfile == ROBOT || IsVehicle( this ) )
return 0;
// we are pros at buying from a vending machine
return 100;
}
break;
default:
break;
}
return 0;
}
INT32 CheckBleeding( SOLDIERTYPE *pSoldier )
{
INT8 bBandaged; //,savedOurTurn;
@@ -20275,6 +20417,11 @@ void SOLDIERTYPE::EVENT_SoldierApplyItemToPerson( INT32 sGridNo, UINT8 ubDirecti
}
}
void SOLDIERTYPE::EVENT_SoldierInteractiveAction( INT32 sGridNo, UINT16 usActionType )
{
DoInteractiveAction( sGridNo, this );
}
void SOLDIERTYPE::EVENT_SoldierBeginReloadRobot( INT32 sGridNo, UINT8 ubDirection, UINT8 ubMercSlot )
{
UINT8 ubPerson;
+6
View File
@@ -231,6 +231,7 @@ enum
MERC_BUILD_FORTIFICATION,
MERC_HANDCUFF_PERSON,
MERC_APPLYITEM,
MERC_INTERACTIVEACTION,
};
// ENUMERATIONS FOR THROW ACTIONS
@@ -565,6 +566,7 @@ enum
MTA_NONE = 0,
MTA_FORTIFY,
MTA_REMOVE_FORTIFY,
MTA_HACK,
NUM_MTA,
};
@@ -1626,6 +1628,7 @@ public:
void EVENT_SoldierBuildStructure( INT32 sGridNo, UINT8 ubDirection ); // added by Flugente
void EVENT_SoldierHandcuffPerson( INT32 sGridNo, UINT8 ubDirection ); // added by Flugente
void EVENT_SoldierApplyItemToPerson( INT32 sGridNo, UINT8 ubDirection ); // added by Flugente
void EVENT_SoldierInteractiveAction( INT32 sGridNo, UINT16 usActionType ); // added by Flugente
BOOLEAN EVENT_InternalGetNewSoldierPath( INT32 sDestGridNo, UINT16 usMovementAnim, BOOLEAN fFromUI, BOOLEAN fForceRestart );
void EVENT_InternalSetSoldierDestination( UINT16 usNewDirection, BOOLEAN fFromMove, UINT16 usAnimState );
@@ -1935,6 +1938,9 @@ public:
// Flugente: chance to defeat a water snake instead of being hit by it
UINT8 GetWaterSnakeDefenseChance( );
// Flugente: interactive actions
UINT16 GetInteractiveActionSkill( INT32 sGridNo, UINT8 usLevel, UINT16 usType );
//////////////////////////////////////////////////////////////////////////////
}; // SOLDIERTYPE;
+130
View File
@@ -12,6 +12,7 @@
#include "renderworld.h"
#include "strategicmap.h"
#include "rotting corpses.h"
#include "WorldDat.h" // added by Flugente
#endif
extern BOOLEAN DoesSAMExistHere( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ, INT32 sGridNo );
@@ -590,6 +591,135 @@ BOOLEAN IsFortificationPossibleAtGridNo( INT32 sGridNo )
return( pStructure == NULL );
}
UINT16 InteractiveActionPossibleAtGridNo( INT32 sGridNo, UINT8 usLevel, UINT16& arusStructIndex )
{
arusStructIndex = 0;
STRUCTURE * pStruct = FindStructure( sGridNo, (STRUCTURE_GENERIC) );
if ( pStruct )
{
// if this is a multi-tile structure, be sure to use the base gridno
if ( !(pStruct->fFlags & STRUCTURE_BASE_TILE) )
{
pStruct = FindBaseStructure( pStruct );
if ( !pStruct )
return INTERACTIVE_STRUCTURE_NO_ACTION;
}
LEVELNODE* pNode = FindLevelNodeBasedOnStructure( pStruct->sGridNo, pStruct );
if ( pNode )
{
UINT16 usIndex = pNode->usIndex;
UINT32 uiTileType = 0;
if ( GetTileType( usIndex, &uiTileType ) )
{
// we loop over each action and determine whether it fits
// multiple actions can fit, we chose whatever fits best
// So, for example, we can set computers everywhere to be hackable with a generci result, and then define more precise results on specific computers
// Other example: With just oen entry, with neither sector nor location set, we can make all water taps drinkable
BOOLEAN foundmatch_precise = FALSE;
BOOLEAN foundmatch_sector = FALSE;
BOOLEAN foundmatch_any = FALSE;
UINT8 sector = SECTOR( gWorldSectorX, gWorldSectorY );
int foundindex = 0;
if ( gTilesets[giCurrentTilesetID].TileSurfaceFilenames[uiTileType][0] )
{
for ( int i = 0; i < gMaxInteractiveStructureRead; ++i )
{
if ( !_strnicmp( gTilesets[giCurrentTilesetID].TileSurfaceFilenames[uiTileType], gInteractiveStructure[i].szTileSetName, 11 ) )
{
std::vector<UINT16> tmpvec = gInteractiveStructure[i].tileindexvector;
std::vector<UINT16>::iterator it = std::find( tmpvec.begin( ), tmpvec.end( ), pStruct->pDBStructureRef->pDBStructure->usStructureNumber );
if ( it != tmpvec.end( ) )
{
if ( gInteractiveStructure[i].sector == sector && gInteractiveStructure[i].sectorlevel == gbWorldSectorZ )
{
foundmatch_sector = TRUE;
foundindex = i;
if ( gInteractiveStructure[i].sLevel == usLevel || gInteractiveStructure[i].sLevel == -1 )
{
std::vector<INT32> tmpgridnovec = gInteractiveStructure[i].gridnovector;
std::vector<INT32>::iterator it_gridno = std::find( tmpgridnovec.begin( ), tmpgridnovec.end( ), pStruct->sGridNo );
if ( it_gridno != tmpgridnovec.end( ) )
{
foundmatch_precise = TRUE;
break;
}
}
}
// some actions cen be defined everywhere
else if ( !foundmatch_sector && gInteractiveStructure[i].sector == -1 && gInteractiveStructure[i].sectorlevel == -1 )
{
foundmatch_any = TRUE;
foundindex = i;
}
}
}
}
}
// otherwise, check first tileset (GENERIC 1)
else if ( gTilesets[0].TileSurfaceFilenames[uiTileType][0] )
{
for ( int i = 0; i < gMaxInteractiveStructureRead; ++i )
{
if ( !_strnicmp( gTilesets[0].TileSurfaceFilenames[uiTileType], gInteractiveStructure[i].szTileSetName, 11 ) )
{
std::vector<UINT16> tmpvec = gInteractiveStructure[i].tileindexvector;
std::vector<UINT16>::iterator it = std::find( tmpvec.begin( ), tmpvec.end( ), pStruct->pDBStructureRef->pDBStructure->usStructureNumber );
if ( it != tmpvec.end() )
{
if ( gInteractiveStructure[i].sector == sector && gInteractiveStructure[i].sectorlevel == gbWorldSectorZ )
{
foundmatch_sector = TRUE;
foundindex = i;
if ( gInteractiveStructure[i].sLevel == usLevel || gInteractiveStructure[i].sLevel == -1 )
{
std::vector<INT32> tmpgridnovec = gInteractiveStructure[i].gridnovector;
std::vector<INT32>::iterator it_gridno = std::find( tmpgridnovec.begin( ), tmpgridnovec.end( ), pStruct->sGridNo );
if ( it_gridno != tmpgridnovec.end() )
{
foundmatch_precise = TRUE;
break;
}
}
}
else if ( !foundmatch_sector && gInteractiveStructure[i].sector == -1 && gInteractiveStructure[i].sectorlevel == -1 )
{
foundmatch_any = TRUE;
foundindex = i;
}
}
}
}
}
if ( foundmatch_any || foundmatch_sector || foundmatch_precise )
{
arusStructIndex = foundindex;
return gInteractiveStructure[foundindex].sActionType;
}
}
}
}
return INTERACTIVE_STRUCTURE_NO_ACTION;
}
BOOLEAN IsCutWireFenceAtGridNo( INT32 sGridNo )
{
+1 -1
View File
@@ -39,7 +39,7 @@ BOOLEAN IsCutWireFenceAtGridNo( INT32 sGridNo );
BOOLEAN IsRepairableStructAtGridNo( INT32 sGridNo, UINT8 *pubID );
BOOLEAN IsRefuelableStructAtGridNo( INT32 sGridNo, UINT8 *pubID );
BOOLEAN IsFortificationPossibleAtGridNo( INT32 sGridNo ); // added by Flugente
UINT16 InteractiveActionPossibleAtGridNo( INT32 sGridNo, UINT8 usLevel, UINT16& arusStructIndex ); // added by Flugente
BOOLEAN IsRoofPresentAtGridNo( INT32 sGridNo );
+4
View File
@@ -1144,6 +1144,10 @@
RelativePath=".\XML_IncompatibleAttachments.cpp"
>
</File>
<File
RelativePath=".\XML_InteractiveTiles.cpp"
>
</File>
<File
RelativePath=".\XML_ItemAdjustments.cpp"
>
+4
View File
@@ -1146,6 +1146,10 @@
RelativePath="XML_IncompatibleAttachments.cpp"
>
</File>
<File
RelativePath="XML_InteractiveTiles.cpp"
>
</File>
<File
RelativePath=".\XML_ItemAdjustments.cpp"
>
+1
View File
@@ -223,6 +223,7 @@
<ClCompile Include="XML_HiddenNames.cpp" />
<ClCompile Include="XML_IMPItemChoices.cpp" />
<ClCompile Include="XML_IncompatibleAttachments.cpp" />
<ClCompile Include="XML_InteractiveTiles.cpp" />
<ClCompile Include="XML_ItemAdjustments.cpp" />
<ClCompile Include="XML_Keys.cpp" />
<ClCompile Include="XML_Launchable.cpp" />
+3
View File
@@ -562,6 +562,9 @@
</ClCompile>
<ClCompile Include="XML_IncompatibleAttachments.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="XML_InteractiveTiles.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="XML_Launchable.cpp">
<Filter>Source Files</Filter>
+1
View File
@@ -224,6 +224,7 @@
<ClCompile Include="XML_HiddenNames.cpp" />
<ClCompile Include="XML_IMPItemChoices.cpp" />
<ClCompile Include="XML_IncompatibleAttachments.cpp" />
<ClCompile Include="XML_InteractiveTiles.cpp" />
<ClCompile Include="XML_ItemAdjustments.cpp" />
<ClCompile Include="XML_Keys.cpp" />
<ClCompile Include="XML_Launchable.cpp" />
+3
View File
@@ -701,5 +701,8 @@
<ClCompile Include="XML_MilitiaIndividual.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="XML_InteractiveTiles.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
+1 -1
View File
@@ -467,7 +467,7 @@ void EndTurnEvents( void )
UINT32 cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; ++cnt, ++pSoldier)
{
if ( pSoldier->bActive && pSoldier->stats.bLife > 0 && !( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) && !( AM_A_ROBOT( pSoldier ) ) )
if ( pSoldier->bActive && pSoldier->stats.bLife > 0 )//&& !( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE ) && !( AM_A_ROBOT( pSoldier ) ) )
{
// Flugente: update multi-turn actions
pSoldier->UpdateMultiTurnAction();
+43 -7
View File
@@ -57,6 +57,7 @@ UINT8 HandleTinCanCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorF
UINT8 HandleFortificationCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ); //added by Flugente
UINT8 HandleHandcuffCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ); //added by Flugente
UINT8 HandleApplyItemCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ); //added by Flugente
UINT8 HandleHackCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags ); //added by Flugente
extern BOOLEAN HandleCheckForBadChangeToGetThrough( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pTargetSoldier, INT32 sTargetGridNo , INT8 bLevel );
@@ -323,11 +324,13 @@ UINT8 GetProperItemCursor( UINT8 ubSoldierID, UINT16 ubItemIndex, INT32 usMapPos
ubCursorID = HandleApplyItemCursor( pSoldier, sTargetGridNo, uiCursorFlags );
break;
case INVALIDCURS:
ubCursorID = INVALID_ACTION_UICURSOR;
case INTERACTIVEACTIONCURS:
ubCursorID = HandleHackCursor( pSoldier, sTargetGridNo, uiCursorFlags );
break;
case INVALIDCURS:
ubCursorID = INVALID_ACTION_UICURSOR;
break;
}
if ( !( gTacticalStatus.uiFlags & INCOMBAT ) )
@@ -341,12 +344,10 @@ UINT8 GetProperItemCursor( UINT8 ubSoldierID, UINT16 ubItemIndex, INT32 usMapPos
PauseRT( FALSE );
}
}
return( ubCursorID );
}
// WANNE: Shows the target cursor over the enemy soldier
UINT8 HandleActivatedTargetCursor( SOLDIERTYPE *pSoldier, INT32 usMapPos, BOOLEAN fShowAPs, BOOLEAN fRecalc, UINT32 uiCursorFlags )
{
@@ -2276,6 +2277,27 @@ UINT8 HandleApplyItemCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCurs
return( APPLYITEM_RED_UICURSOR );
}
UINT8 HandleHackCursor( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT32 uiCursorFlags )
{
// DRAW PATH TO GUY
HandleUIMovementCursor( pSoldier, uiCursorFlags, sGridNo, MOVEUI_TARGET_INTERACTIVEACTION );
UINT16 structindex;
UINT16 possibleaction = InteractiveActionPossibleAtGridNo( sGridNo, pSoldier->pathing.bLevel, structindex );
UINT16 skill = pSoldier->GetInteractiveActionSkill( sGridNo, pSoldier->pathing.bLevel, possibleaction );
if ( possibleaction == INTERACTIVE_STRUCTURE_HACKABLE )
return skill ? HACK_GREY_UICURSOR : HACK_RED_UICURSOR;
else if ( possibleaction == INTERACTIVE_STRUCTURE_READFILE )
return skill ? READFILE_GREY_UICURSOR : READFILE_RED_UICURSOR;
else if ( possibleaction == INTERACTIVE_STRUCTURE_WATERTAP )
return skill ? WATERTAP_GREY_UICURSOR : WATERTAP_RED_UICURSOR;
else if ( possibleaction == INTERACTIVE_STRUCTURE_SODAMACHINE )
return skill ? SODAMACHINE_GREY_UICURSOR : SODAMACHINE_RED_UICURSOR;
return NO_UICURSOR;
}
void HandleEndConfirmCursor( SOLDIERTYPE *pSoldier )
{
UINT16 usInHand;
@@ -2784,7 +2806,7 @@ UINT8 GetActionModeCursor( SOLDIERTYPE *pSoldier )
else
return ( TRAJECTORYCURS );
}
if ( pSoldier->bWeaponMode == WM_ATTACHED_GL_AUTO || ( pSoldier->bWeaponMode == WM_AUTOFIRE && Item[pSoldier->inv[HANDPOS].usItem].grenadelauncher ) )
else if ( pSoldier->bWeaponMode == WM_ATTACHED_GL_AUTO || ( pSoldier->bWeaponMode == WM_AUTOFIRE && Item[pSoldier->inv[HANDPOS].usItem].grenadelauncher ) )
{
if ( gGameSettings.fOptions [ TOPTION_GL_BURST_CURSOR ] )
return( TARGETCURS );
@@ -2823,6 +2845,20 @@ UINT8 GetActionModeCursor( SOLDIERTYPE *pSoldier )
if ( gGameExternalOptions.fAllowPrisonerSystem && HasItemFlag(usInHand, HANDCUFFS) )
ubCursor = HANDCUFFCURS;
// Flugente: interactive actions
// we only check whether an action is possible in principle, not whether this particular guy can do it. That way we know an action is possible here even if we can't perform it at the moment.
// only do this if the item doesn't already allow us to do something else
if ( ubCursor == INVALIDCURS || usInHand == NONE )
{
INT32 usMapPos = NOWHERE;
GetMouseMapPos( &usMapPos );
UINT16 structindex;
UINT16 possibleaction = InteractiveActionPossibleAtGridNo( usMapPos, pSoldier->pathing.bLevel, structindex );
if ( possibleaction > INTERACTIVE_STRUCTURE_NO_ACTION )
ubCursor = INTERACTIVEACTIONCURS;
}
// Flugente: apply misc items to other soldiers
if ( ItemCanBeAppliedToOthers( usInHand ) )
{
+5
View File
@@ -172,6 +172,7 @@ typedef PARSE_STAGE;
#define SECTORLEVEL1NAMESFILENAME "Map\\SectorNamesLevel_1.xml"
#define SECTORLEVEL2NAMESFILENAME "Map\\SectorNamesLevel_2.xml"
#define SECTORLEVEL3NAMESFILENAME "Map\\SectorNamesLevel_3.xml"
#define INTERACTIVEACTIONSFILENAME "Map\\InteractiveActions.xml"
#define GARRISONFILENAME "Army\\GarrisonGroups.xml"
#define PATROLFILENAME "Army\\PatrolGroups.xml"
@@ -326,6 +327,10 @@ extern BOOLEAN WriteStructureDeconstructStats();
extern BOOLEAN ReadInStructureConstructStats(STR fileName);
extern BOOLEAN WriteStructureConstructStats();
// Flugente: interactive actions
extern BOOLEAN ReadInInteractiveActionsStats( STR fileName );
extern BOOLEAN WriteInteractiveActionsStats( );
// Flugente: merchants
extern BOOLEAN ReadInMerchantStats(STR fileName);
extern BOOLEAN WriteMerchantStats();
+6
View File
@@ -135,6 +135,7 @@ backgroundStartElementHandle(void *userData, const XML_Char *name, const XML_Cha
strcmp(name, "smoker" ) == 0 ||
strcmp(name, "croucheddefense" ) == 0 ||
strcmp(name, "fortify_assignment" ) == 0 ||
strcmp(name, "hackerskill" ) == 0 ||
strcmp(name, "druguse") == 0 ||
strcmp(name, "xenophobic") == 0 ||
strcmp(name, "corruptionspread") == 0 ||
@@ -579,6 +580,11 @@ backgroundEndElementHandle(void *userData, const XML_Char *name)
pData->curElement = ELEMENT;
pData->curBackground.value[BG_FORTIFY_ASSIGNMENT] = min( 200, max( -50, (INT16)atol( pData->szCharData ) ) );
}
else if ( strcmp( name, "hackerskill" ) == 0 )
{
pData->curElement = ELEMENT;
pData->curBackground.value[BG_HACKERSKILL] = min( 100, max( 0, (INT16)atol( pData->szCharData ) ) );
}
else if(strcmp(name, "druguse") == 0)
{
pData->curElement = ELEMENT;
+260
View File
@@ -0,0 +1,260 @@
/**
* @file
* @author Flugente (bears-pit.com)
*/
#ifdef PRECOMPILEDHEADERS
#include "Tactical All.h"
#else
#include "sgp.h"
#include "Debug Control.h"
#include "expat.h"
#include "gamesettings.h"
#include "XML.h"
#include "FileMan.h"
#include "Handle Items.h"
#endif
INTERACTIVE_STRUCTURE gInteractiveStructure[INTERACTIVE_STRUCTURE_MAX];
UINT32 gMaxInteractiveStructureRead = 0;
struct
{
PARSE_STAGE curElement;
CHAR8 szCharData[MAX_CHAR_DATA_LENGTH + 1];
INTERACTIVE_STRUCTURE curFood;
INTERACTIVE_STRUCTURE* curArray;
UINT32 maxArraySize;
UINT32 curIndex;
UINT32 currentDepth;
UINT32 maxReadDepth;
}
typedef interactiveactionsParseData;
static void XMLCALL
interactiveactionsStartElementHandle( void *userData, const XML_Char *name, const XML_Char **atts )
{
interactiveactionsParseData * pData = (interactiveactionsParseData *)userData;
if ( pData->currentDepth <= pData->maxReadDepth ) //are we reading this element?
{
if ( strcmp( name, "INTERACTIVEACTIONS" ) == 0 && pData->curElement == ELEMENT_NONE )
{
pData->curElement = ELEMENT_LIST;
memset( pData->curArray, 0, sizeof(INTERACTIVE_STRUCTURE)*pData->maxArraySize );
pData->maxReadDepth++; //we are not skipping this element
}
else if ( strcmp( name, "ACTION" ) == 0 && pData->curElement == ELEMENT_LIST )
{
pData->curElement = ELEMENT;
memset( &pData->curFood, 0, sizeof(INTERACTIVE_STRUCTURE) );
pData->curFood.reset( );
pData->maxReadDepth++; //we are not skipping this element
}
else if ( pData->curElement == ELEMENT &&
(strcmp( name, "SectorGrid" ) == 0 ||
strcmp( name, "sectorlevel" ) == 0 ||
strcmp( name, "szTileSetName" ) == 0 ||
strcmp( name, "usTileIndex" ) == 0 ||
strcmp( name, "sStructureGridno" ) == 0 ||
strcmp( name, "sLevel" ) == 0 ||
strcmp( name, "sActionType" ) == 0 ||
strcmp( name, "difficulty" ) == 0 ||
strcmp( name, "luaactionid" ) == 0
) )
{
pData->curElement = ELEMENT_PROPERTY;
pData->maxReadDepth++; //we are not skipping this element
}
pData->szCharData[0] = '\0';
}
pData->currentDepth++;
}
static void XMLCALL
interactiveactionsDataHandle( void *userData, const XML_Char *str, int len )
{
interactiveactionsParseData * pData = (interactiveactionsParseData *)userData;
if ( (pData->currentDepth <= pData->maxReadDepth) &&
(strlen( pData->szCharData ) < MAX_CHAR_DATA_LENGTH)
)
{
strncat( pData->szCharData, str, __min( (unsigned int)len, MAX_CHAR_DATA_LENGTH - strlen( pData->szCharData ) ) );
}
}
static void XMLCALL
interactiveactionsEndElementHandle( void *userData, const XML_Char *name )
{
static std::vector<UINT16> tileindexvector;
static std::vector<INT32> gridnovector;
interactiveactionsParseData * pData = (interactiveactionsParseData *)userData;
if ( pData->currentDepth <= pData->maxReadDepth ) //we're at the end of an element that we've been reading
{
if ( strcmp( name, "INTERACTIVEACTIONS" ) == 0 )
{
pData->curElement = ELEMENT_NONE;
gMaxInteractiveStructureRead = pData->curIndex;
}
else if ( strcmp( name, "ACTION" ) == 0 )
{
pData->curElement = ELEMENT_LIST;
if ( pData->curIndex < pData->maxArraySize )
{
// for whatever reasons the game crashes in VS2008 Release builds when copying over the tilevector
// this seems odd, as this works just fine in VS2010 and VS2013, and also works in VS205 debug builds
// for now, copy over the content by hand
pData->curArray[pData->curIndex].sector = pData->curFood.sector;
pData->curArray[pData->curIndex].sectorlevel = pData->curFood.sectorlevel;
strncpy( pData->curArray[pData->curIndex].szTileSetName, pData->curFood.szTileSetName, 20 );
pData->curArray[pData->curIndex].tileindexvector = tileindexvector;
pData->curArray[pData->curIndex].gridnovector = gridnovector;
pData->curArray[pData->curIndex].sLevel = pData->curFood.sLevel;
pData->curArray[pData->curIndex].sActionType = pData->curFood.sActionType;
pData->curArray[pData->curIndex].difficulty = pData->curFood.difficulty;
pData->curArray[pData->curIndex].luaactionid = pData->curFood.luaactionid;
tileindexvector.clear( );
tileindexvector.resize( 0 );
gridnovector.clear();
gridnovector.resize( 0 );
}
pData->curIndex++;
}
else if ( strcmp( name, "SectorGrid" ) == 0 )
{
UINT8 x, y;
pData->curElement = ELEMENT;
y = (UINT8)pData->szCharData[0] & 0x1F;
x = (UINT8)atol( &pData->szCharData[1] );
if ( x > 0 && x <= 16 && y > 0 && y <= 16 )
{
pData->curFood.sector = SECTOR(x, y);
}
}
else if ( strcmp( name, "sectorlevel" ) == 0 )
{
pData->curElement = ELEMENT;
pData->curFood.sectorlevel = min( 3, max(-1, (INT8)atol( pData->szCharData ) ) );
}
else if ( strcmp( name, "szTileSetName" ) == 0 )
{
pData->curElement = ELEMENT;
strncpy( pData->curFood.szTileSetName, pData->szCharData, 20 );
}
else if ( strcmp( name, "usTileIndex" ) == 0 )
{
pData->curElement = ELEMENT;
tileindexvector.push_back( (UINT16)atol( pData->szCharData ) );
}
else if ( strcmp( name, "sStructureGridno" ) == 0 )
{
pData->curElement = ELEMENT;
gridnovector.push_back( (INT32)atol( pData->szCharData ) );
}
else if ( strcmp( name, "sLevel" ) == 0 )
{
pData->curElement = ELEMENT;
pData->curFood.sLevel = min( 1, max( 0, (INT8)atol( pData->szCharData ) ) );
}
else if ( strcmp( name, "sActionType" ) == 0 )
{
pData->curElement = ELEMENT;
pData->curFood.sActionType = (UINT16)atol( pData->szCharData );
}
else if ( strcmp( name, "difficulty" ) == 0 )
{
pData->curElement = ELEMENT;
pData->curFood.difficulty = (INT32)atol( pData->szCharData );
}
else if ( strcmp( name, "luaactionid" ) == 0 )
{
pData->curElement = ELEMENT;
pData->curFood.luaactionid = (INT32)atol( pData->szCharData );
}
pData->maxReadDepth--;
}
pData->currentDepth--;
}
BOOLEAN ReadInInteractiveActionsStats( STR fileName )
{
HWFILE hFile;
UINT32 uiBytesRead;
UINT32 uiFSize;
CHAR8 * lpcBuffer;
XML_Parser parser = XML_ParserCreate( NULL );
interactiveactionsParseData pData;
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, "Loading InteractiveActions.xml" );
// Open merges file
hFile = FileOpen( fileName, FILE_ACCESS_READ, FALSE );
if ( !hFile )
return(FALSE);
uiFSize = FileGetSize( hFile );
lpcBuffer = (CHAR8 *)MemAlloc( uiFSize + 1 );
//Read in block
if ( !FileRead( hFile, lpcBuffer, uiFSize, &uiBytesRead ) )
{
MemFree( lpcBuffer );
return(FALSE);
}
lpcBuffer[uiFSize] = 0; //add a null terminator
FileClose( hFile );
XML_SetElementHandler( parser, interactiveactionsStartElementHandle, interactiveactionsEndElementHandle );
XML_SetCharacterDataHandler( parser, interactiveactionsDataHandle );
memset( &pData, 0, sizeof(pData) );
pData.maxArraySize = INTERACTIVE_STRUCTURE_MAX;
pData.curIndex = 0;
pData.curFood.reset( );
pData.curArray = gInteractiveStructure;
XML_SetUserData( parser, &pData );
if ( !XML_Parse( parser, lpcBuffer, uiFSize, TRUE ) )
{
CHAR8 errorBuf[511];
sprintf( errorBuf, "XML Parser Error in InteractiveActions.xml: %s at line %d", XML_ErrorString( XML_GetErrorCode( parser ) ), XML_GetCurrentLineNumber( parser ) );
LiveMessage( errorBuf );
MemFree( lpcBuffer );
return FALSE;
}
MemFree( lpcBuffer );
XML_ParserFree( parser );
return(TRUE);
}
+1 -1
View File
@@ -15,7 +15,7 @@ struct
CHAR8 szCharData[MAX_CHAR_DATA_LENGTH+1];
STRUCTURE_CONSTRUCT curFood;
STRUCTURE_CONSTRUCT * curArray;
STRUCTURE_CONSTRUCT* curArray;
UINT32 maxArraySize;
UINT32 currentDepth;