mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
Backported the "order to attack" function from 4.x
Updated translation ini files. Updated version number.
This commit is contained in:
@@ -14,6 +14,10 @@ NPCPickupFail=%s cannot pick up the item.
|
|||||||
AmmoInfoGlovz=Div: DR/%d, DT/%d
|
AmmoInfoGlovz=Div: DR/%d, DT/%d
|
||||||
AmmoInfoYAAM=DT Mod: %d
|
AmmoInfoYAAM=DT Mod: %d
|
||||||
|
|
||||||
|
PartyOrderAttackHuman=I'll take care of it.|Okay, I got it.|Sounds like a plan.
|
||||||
|
PartyOrderAttackCreature=::Growl::
|
||||||
|
PartyOrderAttackRobot=::Beep::
|
||||||
|
|
||||||
[AppearanceMod]
|
[AppearanceMod]
|
||||||
RaceText=Race
|
RaceText=Race
|
||||||
StyleText=Style
|
StyleText=Style
|
||||||
|
|||||||
+2
-1
@@ -1,5 +1,5 @@
|
|||||||
;sfall configuration settings
|
;sfall configuration settings
|
||||||
;v3.8.38
|
;v3.8.38.1
|
||||||
|
|
||||||
[Main]
|
[Main]
|
||||||
;Set to 1 if you want to use command line arguments to tell sfall to use another ini file
|
;Set to 1 if you want to use command line arguments to tell sfall to use another ini file
|
||||||
@@ -636,6 +636,7 @@ PipboyTimeAnimDelay=50
|
|||||||
;Set to 0 to disable
|
;Set to 0 to disable
|
||||||
;Set to 1 to control all critters in combat (allowed only in sfall debugging mode)
|
;Set to 1 to control all critters in combat (allowed only in sfall debugging mode)
|
||||||
;Set to 2 to control all party members
|
;Set to 2 to control all party members
|
||||||
|
;Set to 3 to let you order party members to attack specified targets after your first attack instead of controlling them directly
|
||||||
ControlCombat=0
|
ControlCombat=0
|
||||||
;If you want to control only specific critters, uncomment the ControlCombatPIDList line and set a comma delimited list of PIDs
|
;If you want to control only specific critters, uncomment the ControlCombatPIDList line and set a comma delimited list of PIDs
|
||||||
;ControlCombatPIDList=62,89,97,107,160,161
|
;ControlCombatPIDList=62,89,97,107,160,161
|
||||||
|
|||||||
Binary file not shown.
@@ -14,6 +14,10 @@ NPCPickupFail=%s n
|
|||||||
AmmoInfoGlovz=Div: DR/%d, DT/%d
|
AmmoInfoGlovz=Div: DR/%d, DT/%d
|
||||||
AmmoInfoYAAM=DT Mod: %d
|
AmmoInfoYAAM=DT Mod: %d
|
||||||
|
|
||||||
|
PartyOrderAttackHuman=Eu tomarei conta disso.|Ok, eu entendi.|Soa como um plano.
|
||||||
|
PartyOrderAttackCreature=::Rosnado::
|
||||||
|
PartyOrderAttackRobot=::Beep::
|
||||||
|
|
||||||
[AppearanceMod]
|
[AppearanceMod]
|
||||||
RaceText=Raça
|
RaceText=Raça
|
||||||
StyleText=Estilo
|
StyleText=Estilo
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ NPCPickupFail=%s
|
|||||||
AmmoInfoGlovz=Div: DR/%d, DT/%d
|
AmmoInfoGlovz=Div: DR/%d, DT/%d
|
||||||
AmmoInfoYAAM=DT Mod: %d
|
AmmoInfoYAAM=DT Mod: %d
|
||||||
|
|
||||||
|
PartyOrderAttackHuman=我会对付他的。|好,我知道了。|好主意。
|
||||||
|
PartyOrderAttackCreature=::吼吼::
|
||||||
|
PartyOrderAttackRobot=::哔::
|
||||||
|
|
||||||
[AppearanceMod]
|
[AppearanceMod]
|
||||||
RaceText=种族
|
RaceText=种族
|
||||||
StyleText=风格
|
StyleText=风格
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ NPCPickupFail=%s
|
|||||||
AmmoInfoGlovz=Div: DR/%d, DT/%d
|
AmmoInfoGlovz=Div: DR/%d, DT/%d
|
||||||
AmmoInfoYAAM=DT Mod: %d
|
AmmoInfoYAAM=DT Mod: %d
|
||||||
|
|
||||||
|
PartyOrderAttackHuman=我會對付他的。|好,我知道了。|好主意。
|
||||||
|
PartyOrderAttackCreature=::吼吼::
|
||||||
|
PartyOrderAttackRobot=::嗶::
|
||||||
|
|
||||||
[AppearanceMod]
|
[AppearanceMod]
|
||||||
RaceText=種族
|
RaceText=種族
|
||||||
StyleText=風格
|
StyleText=風格
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ NPCPickupFail=%s ne peux pas prendre cet objet.
|
|||||||
AmmoInfoGlovz=Div. : RD/%d, SD/%d
|
AmmoInfoGlovz=Div. : RD/%d, SD/%d
|
||||||
AmmoInfoYAAM=Mod. SD : %d
|
AmmoInfoYAAM=Mod. SD : %d
|
||||||
|
|
||||||
|
PartyOrderAttackHuman=Je m'en occupe.|Okay, je fais ça.|Bonne idée.
|
||||||
|
PartyOrderAttackCreature=::Grogne::
|
||||||
|
PartyOrderAttackRobot=::Biip::
|
||||||
|
|
||||||
[AppearanceMod]
|
[AppearanceMod]
|
||||||
RaceText=Race
|
RaceText=Race
|
||||||
StyleText=Style
|
StyleText=Style
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ NPCPickupFail=%s kann das Objekt nicht nehmen.
|
|||||||
AmmoInfoGlovz=Div: RV/%d, SV/%d
|
AmmoInfoGlovz=Div: RV/%d, SV/%d
|
||||||
AmmoInfoYAAM=SV-Mod: %d
|
AmmoInfoYAAM=SV-Mod: %d
|
||||||
|
|
||||||
|
PartyOrderAttackHuman=Ich kümmere mich darum.|Wird gemacht.|Geht klar.
|
||||||
|
PartyOrderAttackCreature=::Grrrr::
|
||||||
|
PartyOrderAttackRobot=::Piep::
|
||||||
|
|
||||||
[AppearanceMod]
|
[AppearanceMod]
|
||||||
RaceText=Geschlecht
|
RaceText=Geschlecht
|
||||||
StyleText=Stil
|
StyleText=Stil
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ NPCPickupFail=%s nie mo
|
|||||||
AmmoInfoGlovz=Dz.: OO/%d, PO/%d
|
AmmoInfoGlovz=Dz.: OO/%d, PO/%d
|
||||||
AmmoInfoYAAM=Mod. PO: %d
|
AmmoInfoYAAM=Mod. PO: %d
|
||||||
|
|
||||||
|
PartyOrderAttackHuman=Zajmę się tym.|Okej, zrozumiałem.|No to mamy plan.
|
||||||
|
PartyOrderAttackCreature=::Warknięcie::
|
||||||
|
PartyOrderAttackRobot=::Bip::
|
||||||
|
|
||||||
[AppearanceMod]
|
[AppearanceMod]
|
||||||
RaceText=Rasa
|
RaceText=Rasa
|
||||||
StyleText=Styl
|
StyleText=Styl
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ NPCPickupFail=%s
|
|||||||
AmmoInfoGlovz=Дел.: СУ/%d, ПУ/%d
|
AmmoInfoGlovz=Дел.: СУ/%d, ПУ/%d
|
||||||
AmmoInfoYAAM=Изм. ПУ: %d
|
AmmoInfoYAAM=Изм. ПУ: %d
|
||||||
|
|
||||||
|
PartyOrderAttackHuman=Хорошо, я им займусь.|Цель ясна.|Действую по плану.
|
||||||
|
PartyOrderAttackCreature=::Гррр::
|
||||||
|
PartyOrderAttackRobot=::Биип::
|
||||||
|
|
||||||
[AppearanceMod]
|
[AppearanceMod]
|
||||||
RaceText=Раса
|
RaceText=Раса
|
||||||
StyleText=Стиль
|
StyleText=Стиль
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ FUNC(SexWindow_, 0x437664)
|
|||||||
FUNC(_word_wrap_, 0x4BC6F0)
|
FUNC(_word_wrap_, 0x4BC6F0)
|
||||||
FUNC(action_get_an_object_, 0x412134)
|
FUNC(action_get_an_object_, 0x412134)
|
||||||
FUNC(action_loot_container_, 0x4123E8)
|
FUNC(action_loot_container_, 0x4123E8)
|
||||||
|
FUNC(action_talk_to_, 0x413330)
|
||||||
FUNC(action_use_an_item_on_object_, 0x411F2C)
|
FUNC(action_use_an_item_on_object_, 0x411F2C)
|
||||||
FUNC(action_use_skill_on_, 0x41255C)
|
FUNC(action_use_skill_on_, 0x41255C)
|
||||||
FUNC(add_bar_box_, 0x4616F0)
|
FUNC(add_bar_box_, 0x4616F0)
|
||||||
@@ -49,6 +50,7 @@ FUNC(adjust_fid_, 0x4716E8)
|
|||||||
FUNC(ai_can_use_weapon_, 0x4298EC) // (GameObject *aCritter<eax>, int aWeapon<edx>, int a2Or3<ebx>) returns 1 or 0
|
FUNC(ai_can_use_weapon_, 0x4298EC) // (GameObject *aCritter<eax>, int aWeapon<edx>, int a2Or3<ebx>) returns 1 or 0
|
||||||
FUNC(ai_cap_, 0x4280B4)
|
FUNC(ai_cap_, 0x4280B4)
|
||||||
FUNC(ai_check_drugs_, 0x428480)
|
FUNC(ai_check_drugs_, 0x428480)
|
||||||
|
FUNC(ai_danger_source_, 0x428F4C)
|
||||||
FUNC(ai_have_ammo_, 0x4292D4)
|
FUNC(ai_have_ammo_, 0x4292D4)
|
||||||
FUNC(ai_magic_hands_, 0x428398)
|
FUNC(ai_magic_hands_, 0x428398)
|
||||||
FUNC(ai_pick_hit_mode_, 0x429DB4)
|
FUNC(ai_pick_hit_mode_, 0x429DB4)
|
||||||
|
|||||||
@@ -112,6 +112,7 @@
|
|||||||
#define FO_VAR_gdNumOptions 0x5186D8
|
#define FO_VAR_gdNumOptions 0x5186D8
|
||||||
#define FO_VAR_gIsSteal 0x51D430
|
#define FO_VAR_gIsSteal 0x51D430
|
||||||
#define FO_VAR_glblmode 0x5709D0
|
#define FO_VAR_glblmode 0x5709D0
|
||||||
|
#define FO_VAR_gmouse_3d_action_nums 0x518D1E
|
||||||
#define FO_VAR_gmouse_3d_current_mode 0x518D38
|
#define FO_VAR_gmouse_3d_current_mode 0x518D38
|
||||||
#define FO_VAR_gmouse_current_cursor 0x518C0C
|
#define FO_VAR_gmouse_current_cursor 0x518C0C
|
||||||
#define FO_VAR_gmovie_played_list 0x596C78
|
#define FO_VAR_gmovie_played_list 0x596C78
|
||||||
|
|||||||
@@ -14,6 +14,59 @@ namespace game
|
|||||||
|
|
||||||
namespace sf = sfall;
|
namespace sf = sfall;
|
||||||
|
|
||||||
|
// same as obj_blocking_at_ engine function, but ignores objects of the critter type
|
||||||
|
static fo::GameObject* __fastcall obj_path_blocking_at(fo::GameObject* source, long tile, long elev) {
|
||||||
|
if (tile < 0 || tile >= 40000) return nullptr;
|
||||||
|
|
||||||
|
fo::ObjectTable* obj = fo::ptr::objectTable[tile];
|
||||||
|
while (obj) {
|
||||||
|
if (elev == obj->object->elevation) {
|
||||||
|
fo::GameObject* object = obj->object;
|
||||||
|
long flags = object->flags;
|
||||||
|
if (!(flags & (fo::ObjectFlag::Hidden | fo::ObjectFlag::NoBlock)) && source != object) {
|
||||||
|
char type = object->TypeFid();
|
||||||
|
if (type == fo::ObjType::OBJ_TYPE_SCENERY || type == fo::ObjType::OBJ_TYPE_WALL) {
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obj = obj->nextObject;
|
||||||
|
}
|
||||||
|
// check for multihex objects on the tile
|
||||||
|
long direction = 0;
|
||||||
|
do {
|
||||||
|
long _tile = fo::func::tile_num_in_direction(tile, direction, 1);
|
||||||
|
if (_tile >= 0 && _tile < 40000) {
|
||||||
|
obj = fo::ptr::objectTable[_tile];
|
||||||
|
while (obj) {
|
||||||
|
if (elev == obj->object->elevation) {
|
||||||
|
fo::GameObject* object = obj->object;
|
||||||
|
long flags = object->flags;
|
||||||
|
if (flags & fo::ObjectFlag::MultiHex && !(flags & (fo::ObjectFlag::Hidden | fo::ObjectFlag::NoBlock)) && source != object) {
|
||||||
|
char type = object->TypeFid();
|
||||||
|
if (type == fo::ObjType::OBJ_TYPE_SCENERY || type == fo::ObjType::OBJ_TYPE_WALL) {
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
obj = obj->nextObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (++direction < 6);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void __declspec(naked) Tilemap::obj_path_blocking_at_() {
|
||||||
|
__asm {
|
||||||
|
push ecx;
|
||||||
|
push ebx;
|
||||||
|
mov ecx, eax;
|
||||||
|
call obj_path_blocking_at;
|
||||||
|
pop ecx;
|
||||||
|
retn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//static std::vector<int> buildLineTiles;
|
//static std::vector<int> buildLineTiles;
|
||||||
|
|
||||||
//static bool TileExists(long tile) {
|
//static bool TileExists(long tile) {
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ class Tilemap {
|
|||||||
public:
|
public:
|
||||||
static void init();
|
static void init();
|
||||||
|
|
||||||
|
static void obj_path_blocking_at_();
|
||||||
|
|
||||||
static long __fastcall tile_num_beyond(long sourceTile, long targetTile, long maxRange);
|
static long __fastcall tile_num_beyond(long sourceTile, long targetTile, long maxRange);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -552,6 +552,7 @@ static void __fastcall CombatInternal(fo::CombatGcsd* gcsd) {
|
|||||||
|
|
||||||
// OnCombatEnd
|
// OnCombatEnd
|
||||||
AI::AICombatClear();
|
AI::AICombatClear();
|
||||||
|
PartyControl::OnCombatEnd();
|
||||||
SetInLoop(0, COMBAT);
|
SetInLoop(0, COMBAT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include "..\main.h"
|
#include "..\main.h"
|
||||||
#include "..\FalloutEngine\Fallout2.h"
|
#include "..\FalloutEngine\Fallout2.h"
|
||||||
#include "ExtraSaveSlots.h"
|
#include "ExtraSaveSlots.h"
|
||||||
|
#include "PartyControl.h"
|
||||||
|
|
||||||
#include "MetaruleExtender.h"
|
#include "MetaruleExtender.h"
|
||||||
|
|
||||||
@@ -41,6 +42,8 @@ enum MetaruleFunction : long {
|
|||||||
GET_CURRENT_QSAVE_PAGE = 212,
|
GET_CURRENT_QSAVE_PAGE = 212,
|
||||||
GET_CURRENT_QSAVE_SLOT = 213,
|
GET_CURRENT_QSAVE_SLOT = 213,
|
||||||
SET_CURRENT_QSAVE_SLOT = 214,
|
SET_CURRENT_QSAVE_SLOT = 214,
|
||||||
|
|
||||||
|
PARTY_ORDER_ATTACK = 999,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -67,6 +70,9 @@ static long __fastcall op_metarule3_ext(long metafunc, long* args) {
|
|||||||
case CLEAR_KEYBOARD_BUFFER:
|
case CLEAR_KEYBOARD_BUFFER:
|
||||||
__asm call fo::funcoffs::kb_clear_;
|
__asm call fo::funcoffs::kb_clear_;
|
||||||
break;
|
break;
|
||||||
|
case PARTY_ORDER_ATTACK:
|
||||||
|
PartyControl::OrderAttackPatch();
|
||||||
|
break;
|
||||||
case GET_CURRENT_SAVE_SLOT:
|
case GET_CURRENT_SAVE_SLOT:
|
||||||
result = ExtraSaveSlots::GetSaveSlot();
|
result = ExtraSaveSlots::GetSaveSlot();
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -26,6 +26,9 @@
|
|||||||
#include "..\FalloutEngine\Fallout2.h"
|
#include "..\FalloutEngine\Fallout2.h"
|
||||||
#include "..\Translate.h"
|
#include "..\Translate.h"
|
||||||
|
|
||||||
|
#include "..\Game\objects.h"
|
||||||
|
#include "..\Game\tilemap.h"
|
||||||
|
|
||||||
#include "PartyControl.h"
|
#include "PartyControl.h"
|
||||||
|
|
||||||
namespace sfall
|
namespace sfall
|
||||||
@@ -458,6 +461,193 @@ static void __declspec(naked) gdControlUpdateInfo_hook() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static bool partyOrderPickTargetLoop;
|
||||||
|
static std::vector<std::string> partyOrderAttackMsg;
|
||||||
|
|
||||||
|
struct PartyTarget {
|
||||||
|
fo::GameObject* member;
|
||||||
|
fo::GameObject* target;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::vector<PartyTarget> partyOrderAttackTarget;
|
||||||
|
|
||||||
|
static fo::GameObject* GetMemberTarget(fo::GameObject* member) {
|
||||||
|
for (std::vector<PartyTarget>::iterator it = partyOrderAttackTarget.begin(); it != partyOrderAttackTarget.end(); ++it) {
|
||||||
|
if (it->member->id == member->id) {
|
||||||
|
fo::GameObject* target = it->target;
|
||||||
|
if (target && target->critter.IsDead()) it->target = nullptr;
|
||||||
|
return it->target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetMemberTarget(fo::GameObject* member, fo::GameObject* target) {
|
||||||
|
for (std::vector<PartyTarget>::iterator it = partyOrderAttackTarget.begin(); it != partyOrderAttackTarget.end(); ++it) {
|
||||||
|
if (it->member->id == member->id) {
|
||||||
|
it->target = target;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PartyTarget pt = { member, target };
|
||||||
|
partyOrderAttackTarget.push_back(pt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// disables the display of the hit chance value when picking a target
|
||||||
|
static void __declspec(naked) gmouse_bk_process_hack() {
|
||||||
|
__asm {
|
||||||
|
mov edx, -1; // mode
|
||||||
|
mov eax, ds:[FO_VAR_gmouse_3d_current_mode];
|
||||||
|
test partyOrderPickTargetLoop, 0xFF;
|
||||||
|
cmovnz eax, edx;
|
||||||
|
retn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __fastcall action_attack_to(long unused, fo::GameObject* partyMember) {
|
||||||
|
fo::func::gmouse_set_cursor(20);
|
||||||
|
fo::func::gmouse_3d_set_mode(2);
|
||||||
|
|
||||||
|
fo::GameObject* targetObject = nullptr;
|
||||||
|
fo::GameObject* validTarget = nullptr;
|
||||||
|
|
||||||
|
long outlineColor; // backup color
|
||||||
|
fo::BoundRect rect;
|
||||||
|
partyOrderPickTargetLoop = true;
|
||||||
|
|
||||||
|
do {
|
||||||
|
fo::GameObject* underObject = fo::func::object_under_mouse(1, 0, *fo::ptr::map_elevation);
|
||||||
|
|
||||||
|
if (targetObject && targetObject != underObject) {
|
||||||
|
targetObject->outline = outlineColor;
|
||||||
|
fo::func::obj_bound(targetObject, &rect);
|
||||||
|
if (!outlineColor) {
|
||||||
|
rect.x--;
|
||||||
|
rect.y--;
|
||||||
|
rect.offx += 2;
|
||||||
|
rect.offy += 2;
|
||||||
|
}
|
||||||
|
fo::func::tile_refresh_rect(&rect, *fo::ptr::map_elevation);
|
||||||
|
targetObject = validTarget = nullptr;
|
||||||
|
}
|
||||||
|
if (underObject && underObject != targetObject && underObject->IsCritter() && underObject->critter.teamNum != partyMember->critter.teamNum) {
|
||||||
|
if (underObject->critter.IsNotDead()) {
|
||||||
|
outlineColor = underObject->outline;
|
||||||
|
|
||||||
|
if ((*fo::ptr::combatNumTurns || underObject->critter.combatState) &&
|
||||||
|
game::Objects::is_within_perception(partyMember, underObject, 0) && // HOOK_WITHINPERCEPTION
|
||||||
|
fo::func::make_path_func(partyMember, partyMember->tile, underObject->tile, 0, 0, game::Tilemap::obj_path_blocking_at_) > 0)
|
||||||
|
{
|
||||||
|
underObject->outline = 254 << 8; // flashing red
|
||||||
|
validTarget = underObject;
|
||||||
|
} else {
|
||||||
|
underObject->outline = 10 << 8; // grey
|
||||||
|
}
|
||||||
|
fo::func::obj_bound(underObject, &rect);
|
||||||
|
fo::func::tile_refresh_rect(&rect, *fo::ptr::map_elevation);
|
||||||
|
targetObject = underObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (validTarget && *fo::ptr::mouse_buttons == 1) break; // left mouse button
|
||||||
|
|
||||||
|
} while (*fo::ptr::mouse_buttons != 2 && fo::func::get_input() != 27); // 27 - escape code
|
||||||
|
|
||||||
|
if (validTarget && *fo::ptr::mouse_buttons == 1) {
|
||||||
|
SetMemberTarget(partyMember, validTarget);
|
||||||
|
validTarget->outline = outlineColor;
|
||||||
|
|
||||||
|
fo::AIcap* cap = fo::func::ai_cap(partyMember);
|
||||||
|
if (cap->disposition == fo::AIpref::Disposition::DISP_custom) {
|
||||||
|
cap->attack_who = fo::AIpref::AttackWho::ATKWHO_whomever;
|
||||||
|
}
|
||||||
|
|
||||||
|
// floating text messages
|
||||||
|
const char* message;
|
||||||
|
switch (fo::func::critter_body_type(partyMember)) {
|
||||||
|
case fo::BodyType::Quadruped: // creatures
|
||||||
|
message = partyOrderAttackMsg[0].c_str();
|
||||||
|
break;
|
||||||
|
case fo::BodyType::Robotic:
|
||||||
|
message = partyOrderAttackMsg[1].c_str();
|
||||||
|
break;
|
||||||
|
default: // biped
|
||||||
|
long max = partyOrderAttackMsg.size() - 1;
|
||||||
|
long rnd = (max > 2) ? fo::func::roll_random(2, max) : 2;
|
||||||
|
message = partyOrderAttackMsg[rnd].c_str();
|
||||||
|
}
|
||||||
|
fo::util::PrintFloatText(partyMember, message, cap->color, cap->outline_color, cap->font);
|
||||||
|
}
|
||||||
|
|
||||||
|
partyOrderPickTargetLoop = false;
|
||||||
|
*fo::ptr::mouse_buttons = 0;
|
||||||
|
|
||||||
|
fo::func::gmouse_set_cursor(0);
|
||||||
|
fo::func::gmouse_3d_set_mode(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __declspec(naked) gmouse_handle_event_hook() {
|
||||||
|
__asm {
|
||||||
|
test ds:[FO_VAR_combat_state], 1;
|
||||||
|
jnz action_attack_to;
|
||||||
|
jmp fo::funcoffs::action_talk_to_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __declspec(naked) gmouse_handle_event_hack() {
|
||||||
|
__asm {
|
||||||
|
test ds:[FO_VAR_combat_state], 1;
|
||||||
|
jnz pick;
|
||||||
|
retn;
|
||||||
|
pick:
|
||||||
|
mov eax, edi; // critter
|
||||||
|
call fo::funcoffs::isPartyMember_;
|
||||||
|
test eax, eax;
|
||||||
|
jz end;
|
||||||
|
mov bl, 1;
|
||||||
|
mov eax, 5;
|
||||||
|
mov [esp + 4], eax; // actionMenuList
|
||||||
|
mov word ptr ds:[FO_VAR_gmouse_3d_action_nums][5*2], 81; // index in INTRFACE.LST (ACTIONI.FRM)
|
||||||
|
end:
|
||||||
|
or eax, 1;
|
||||||
|
retn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __declspec(naked) gmouse_handle_event_hook_restore() {
|
||||||
|
__asm {
|
||||||
|
mov word ptr ds:[FO_VAR_gmouse_3d_action_nums][5*2], 263; // index in INTRFACE.LST (TALKN.FRM)
|
||||||
|
jmp fo::funcoffs::map_enable_bk_processes_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __fastcall SetOrderTarget(fo::GameObject* attacker) {
|
||||||
|
if (attacker->critter.teamNum || partyOrderAttackTarget.empty()) return;
|
||||||
|
fo::GameObject* target = GetMemberTarget(attacker);
|
||||||
|
if (target) attacker->critter.whoHitMe = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __declspec(naked) combat_ai_hook_target() {
|
||||||
|
__asm {
|
||||||
|
push ecx;
|
||||||
|
mov ecx, eax;
|
||||||
|
call SetOrderTarget;
|
||||||
|
pop ecx;
|
||||||
|
mov eax, esi;
|
||||||
|
jmp fo::funcoffs::ai_danger_source_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PartyControl::OrderAttackPatch() {
|
||||||
|
MakeCall(0x44C4A7, gmouse_handle_event_hack, 2);
|
||||||
|
HookCall(0x44C75F, gmouse_handle_event_hook);
|
||||||
|
HookCall(0x44C69A, gmouse_handle_event_hook_restore);
|
||||||
|
MakeCall(0x44B830, gmouse_bk_process_hack);
|
||||||
|
|
||||||
|
HookCall(0x42B235, combat_ai_hook_target);
|
||||||
|
}
|
||||||
|
|
||||||
static void NpcAutoLevelPatch() {
|
static void NpcAutoLevelPatch() {
|
||||||
npcAutoLevelEnabled = IniReader::GetConfigInt("Misc", "NPCAutoLevel", 0) != 0;
|
npcAutoLevelEnabled = IniReader::GetConfigInt("Misc", "NPCAutoLevel", 0) != 0;
|
||||||
if (npcAutoLevelEnabled) {
|
if (npcAutoLevelEnabled) {
|
||||||
@@ -466,6 +656,10 @@ static void NpcAutoLevelPatch() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PartyControl::OnCombatEnd() {
|
||||||
|
partyOrderAttackTarget.clear();
|
||||||
|
}
|
||||||
|
|
||||||
void PartyControl::OnGameLoad() {
|
void PartyControl::OnGameLoad() {
|
||||||
PartyControlReset();
|
PartyControlReset();
|
||||||
if (!npcEngineLevelUp) {
|
if (!npcEngineLevelUp) {
|
||||||
@@ -476,8 +670,12 @@ void PartyControl::OnGameLoad() {
|
|||||||
|
|
||||||
void PartyControl::init() {
|
void PartyControl::init() {
|
||||||
Mode = IniReader::GetConfigInt("Misc", "ControlCombat", 0);
|
Mode = IniReader::GetConfigInt("Misc", "ControlCombat", 0);
|
||||||
if (Mode > 2) Mode = 0;
|
if (Mode >= 3) {
|
||||||
else if (Mode == 1 && !isDebug) Mode = 2;
|
if (Mode == 3) PartyControl::OrderAttackPatch();
|
||||||
|
Mode = 0;
|
||||||
|
} else if (Mode == 1 && !isDebug) {
|
||||||
|
Mode = 2;
|
||||||
|
}
|
||||||
|
|
||||||
if (Mode > 0) {
|
if (Mode > 0) {
|
||||||
dlog("Setting up party control.", DL_INIT);
|
dlog("Setting up party control.", DL_INIT);
|
||||||
@@ -522,6 +720,11 @@ void PartyControl::init() {
|
|||||||
Translate::Get("sfall", "PartyACMsg", "AC:", armorClassMsg, 12);
|
Translate::Get("sfall", "PartyACMsg", "AC:", armorClassMsg, 12);
|
||||||
Translate::Get("sfall", "PartyAddictMsg", "Addict", addictMsg, 16);
|
Translate::Get("sfall", "PartyAddictMsg", "Addict", addictMsg, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
partyOrderAttackMsg.push_back(Translate::Get("sfall", "PartyOrderAttackCreature", "::Growl::", 33));
|
||||||
|
partyOrderAttackMsg.push_back(Translate::Get("sfall", "PartyOrderAttackRobot", "::Beep::", 33));
|
||||||
|
std::vector<std::string> msgs = Translate::GetList("sfall", "PartyOrderAttackHuman", "I'll take care of it.|Okay, I got it.", '|', 512);
|
||||||
|
partyOrderAttackMsg.insert(partyOrderAttackMsg.cend(), msgs.cbegin(), msgs.cend());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ public:
|
|||||||
static void init();
|
static void init();
|
||||||
|
|
||||||
static void OnGameLoad();
|
static void OnGameLoad();
|
||||||
|
static void OnCombatEnd();
|
||||||
|
|
||||||
static int __fastcall SwitchHandHook(fo::GameObject* item);
|
static int __fastcall SwitchHandHook(fo::GameObject* item);
|
||||||
|
|
||||||
@@ -34,6 +35,8 @@ public:
|
|||||||
|
|
||||||
// Returns pointer to "real" dude, which is different from "dude_obj" when controlling another critter
|
// Returns pointer to "real" dude, which is different from "dude_obj" when controlling another critter
|
||||||
static fo::GameObject* RealDudeObject();
|
static fo::GameObject* RealDudeObject();
|
||||||
|
|
||||||
|
static void OrderAttackPatch();
|
||||||
};
|
};
|
||||||
|
|
||||||
extern bool npcAutoLevelEnabled;
|
extern bool npcAutoLevelEnabled;
|
||||||
|
|||||||
+2
-2
@@ -25,6 +25,6 @@
|
|||||||
#define VERSION_MAJOR 3
|
#define VERSION_MAJOR 3
|
||||||
#define VERSION_MINOR 8
|
#define VERSION_MINOR 8
|
||||||
#define VERSION_BUILD 38
|
#define VERSION_BUILD 38
|
||||||
#define VERSION_REV 0
|
#define VERSION_REV 1
|
||||||
|
|
||||||
#define VERSION_STRING "3.8.38"
|
#define VERSION_STRING "3.8.38.1"
|
||||||
|
|||||||
Reference in New Issue
Block a user