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
+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;