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
|
||||
AmmoInfoYAAM=DT Mod: %d
|
||||
|
||||
PartyOrderAttackHuman=I'll take care of it.|Okay, I got it.|Sounds like a plan.
|
||||
PartyOrderAttackCreature=::Growl::
|
||||
PartyOrderAttackRobot=::Beep::
|
||||
|
||||
[AppearanceMod]
|
||||
RaceText=Race
|
||||
StyleText=Style
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
;sfall configuration settings
|
||||
;v3.8.38
|
||||
;v3.8.38.1
|
||||
|
||||
[Main]
|
||||
;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 1 to control all critters in combat (allowed only in sfall debugging mode)
|
||||
;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
|
||||
;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
|
||||
|
||||
Binary file not shown.
@@ -14,6 +14,10 @@ NPCPickupFail=%s n
|
||||
AmmoInfoGlovz=Div: DR/%d, DT/%d
|
||||
AmmoInfoYAAM=DT Mod: %d
|
||||
|
||||
PartyOrderAttackHuman=Eu tomarei conta disso.|Ok, eu entendi.|Soa como um plano.
|
||||
PartyOrderAttackCreature=::Rosnado::
|
||||
PartyOrderAttackRobot=::Beep::
|
||||
|
||||
[AppearanceMod]
|
||||
RaceText=Raça
|
||||
StyleText=Estilo
|
||||
|
||||
@@ -14,6 +14,10 @@ NPCPickupFail=%s
|
||||
AmmoInfoGlovz=Div: DR/%d, DT/%d
|
||||
AmmoInfoYAAM=DT Mod: %d
|
||||
|
||||
PartyOrderAttackHuman=我会对付他的。|好,我知道了。|好主意。
|
||||
PartyOrderAttackCreature=::吼吼::
|
||||
PartyOrderAttackRobot=::哔::
|
||||
|
||||
[AppearanceMod]
|
||||
RaceText=种族
|
||||
StyleText=风格
|
||||
|
||||
@@ -14,6 +14,10 @@ NPCPickupFail=%s
|
||||
AmmoInfoGlovz=Div: DR/%d, DT/%d
|
||||
AmmoInfoYAAM=DT Mod: %d
|
||||
|
||||
PartyOrderAttackHuman=我會對付他的。|好,我知道了。|好主意。
|
||||
PartyOrderAttackCreature=::吼吼::
|
||||
PartyOrderAttackRobot=::嗶::
|
||||
|
||||
[AppearanceMod]
|
||||
RaceText=種族
|
||||
StyleText=風格
|
||||
|
||||
@@ -14,6 +14,10 @@ NPCPickupFail=%s ne peux pas prendre cet objet.
|
||||
AmmoInfoGlovz=Div. : RD/%d, SD/%d
|
||||
AmmoInfoYAAM=Mod. SD : %d
|
||||
|
||||
PartyOrderAttackHuman=Je m'en occupe.|Okay, je fais ça.|Bonne idée.
|
||||
PartyOrderAttackCreature=::Grogne::
|
||||
PartyOrderAttackRobot=::Biip::
|
||||
|
||||
[AppearanceMod]
|
||||
RaceText=Race
|
||||
StyleText=Style
|
||||
|
||||
@@ -14,6 +14,10 @@ NPCPickupFail=%s kann das Objekt nicht nehmen.
|
||||
AmmoInfoGlovz=Div: RV/%d, SV/%d
|
||||
AmmoInfoYAAM=SV-Mod: %d
|
||||
|
||||
PartyOrderAttackHuman=Ich kümmere mich darum.|Wird gemacht.|Geht klar.
|
||||
PartyOrderAttackCreature=::Grrrr::
|
||||
PartyOrderAttackRobot=::Piep::
|
||||
|
||||
[AppearanceMod]
|
||||
RaceText=Geschlecht
|
||||
StyleText=Stil
|
||||
|
||||
@@ -14,6 +14,10 @@ NPCPickupFail=%s nie mo
|
||||
AmmoInfoGlovz=Dz.: OO/%d, PO/%d
|
||||
AmmoInfoYAAM=Mod. PO: %d
|
||||
|
||||
PartyOrderAttackHuman=Zajmę się tym.|Okej, zrozumiałem.|No to mamy plan.
|
||||
PartyOrderAttackCreature=::Warknięcie::
|
||||
PartyOrderAttackRobot=::Bip::
|
||||
|
||||
[AppearanceMod]
|
||||
RaceText=Rasa
|
||||
StyleText=Styl
|
||||
|
||||
@@ -14,6 +14,10 @@ NPCPickupFail=%s
|
||||
AmmoInfoGlovz=Дел.: СУ/%d, ПУ/%d
|
||||
AmmoInfoYAAM=Изм. ПУ: %d
|
||||
|
||||
PartyOrderAttackHuman=Хорошо, я им займусь.|Цель ясна.|Действую по плану.
|
||||
PartyOrderAttackCreature=::Гррр::
|
||||
PartyOrderAttackRobot=::Биип::
|
||||
|
||||
[AppearanceMod]
|
||||
RaceText=Раса
|
||||
StyleText=Стиль
|
||||
|
||||
@@ -41,6 +41,7 @@ FUNC(SexWindow_, 0x437664)
|
||||
FUNC(_word_wrap_, 0x4BC6F0)
|
||||
FUNC(action_get_an_object_, 0x412134)
|
||||
FUNC(action_loot_container_, 0x4123E8)
|
||||
FUNC(action_talk_to_, 0x413330)
|
||||
FUNC(action_use_an_item_on_object_, 0x411F2C)
|
||||
FUNC(action_use_skill_on_, 0x41255C)
|
||||
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_cap_, 0x4280B4)
|
||||
FUNC(ai_check_drugs_, 0x428480)
|
||||
FUNC(ai_danger_source_, 0x428F4C)
|
||||
FUNC(ai_have_ammo_, 0x4292D4)
|
||||
FUNC(ai_magic_hands_, 0x428398)
|
||||
FUNC(ai_pick_hit_mode_, 0x429DB4)
|
||||
|
||||
@@ -112,6 +112,7 @@
|
||||
#define FO_VAR_gdNumOptions 0x5186D8
|
||||
#define FO_VAR_gIsSteal 0x51D430
|
||||
#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_current_cursor 0x518C0C
|
||||
#define FO_VAR_gmovie_played_list 0x596C78
|
||||
|
||||
@@ -14,6 +14,59 @@ namespace game
|
||||
|
||||
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 bool TileExists(long tile) {
|
||||
|
||||
@@ -13,6 +13,8 @@ class Tilemap {
|
||||
public:
|
||||
static void init();
|
||||
|
||||
static void obj_path_blocking_at_();
|
||||
|
||||
static long __fastcall tile_num_beyond(long sourceTile, long targetTile, long maxRange);
|
||||
};
|
||||
|
||||
|
||||
@@ -552,6 +552,7 @@ static void __fastcall CombatInternal(fo::CombatGcsd* gcsd) {
|
||||
|
||||
// OnCombatEnd
|
||||
AI::AICombatClear();
|
||||
PartyControl::OnCombatEnd();
|
||||
SetInLoop(0, COMBAT);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "..\main.h"
|
||||
#include "..\FalloutEngine\Fallout2.h"
|
||||
#include "ExtraSaveSlots.h"
|
||||
#include "PartyControl.h"
|
||||
|
||||
#include "MetaruleExtender.h"
|
||||
|
||||
@@ -41,6 +42,8 @@ enum MetaruleFunction : long {
|
||||
GET_CURRENT_QSAVE_PAGE = 212,
|
||||
GET_CURRENT_QSAVE_SLOT = 213,
|
||||
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:
|
||||
__asm call fo::funcoffs::kb_clear_;
|
||||
break;
|
||||
case PARTY_ORDER_ATTACK:
|
||||
PartyControl::OrderAttackPatch();
|
||||
break;
|
||||
case GET_CURRENT_SAVE_SLOT:
|
||||
result = ExtraSaveSlots::GetSaveSlot();
|
||||
break;
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
#include "..\FalloutEngine\Fallout2.h"
|
||||
#include "..\Translate.h"
|
||||
|
||||
#include "..\Game\objects.h"
|
||||
#include "..\Game\tilemap.h"
|
||||
|
||||
#include "PartyControl.h"
|
||||
|
||||
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() {
|
||||
npcAutoLevelEnabled = IniReader::GetConfigInt("Misc", "NPCAutoLevel", 0) != 0;
|
||||
if (npcAutoLevelEnabled) {
|
||||
@@ -466,6 +656,10 @@ static void NpcAutoLevelPatch() {
|
||||
}
|
||||
}
|
||||
|
||||
void PartyControl::OnCombatEnd() {
|
||||
partyOrderAttackTarget.clear();
|
||||
}
|
||||
|
||||
void PartyControl::OnGameLoad() {
|
||||
PartyControlReset();
|
||||
if (!npcEngineLevelUp) {
|
||||
@@ -476,8 +670,12 @@ void PartyControl::OnGameLoad() {
|
||||
|
||||
void PartyControl::init() {
|
||||
Mode = IniReader::GetConfigInt("Misc", "ControlCombat", 0);
|
||||
if (Mode > 2) Mode = 0;
|
||||
else if (Mode == 1 && !isDebug) Mode = 2;
|
||||
if (Mode >= 3) {
|
||||
if (Mode == 3) PartyControl::OrderAttackPatch();
|
||||
Mode = 0;
|
||||
} else if (Mode == 1 && !isDebug) {
|
||||
Mode = 2;
|
||||
}
|
||||
|
||||
if (Mode > 0) {
|
||||
dlog("Setting up party control.", DL_INIT);
|
||||
@@ -522,6 +720,11 @@ void PartyControl::init() {
|
||||
Translate::Get("sfall", "PartyACMsg", "AC:", armorClassMsg, 12);
|
||||
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 OnGameLoad();
|
||||
static void OnCombatEnd();
|
||||
|
||||
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
|
||||
static fo::GameObject* RealDudeObject();
|
||||
|
||||
static void OrderAttackPatch();
|
||||
};
|
||||
|
||||
extern bool npcAutoLevelEnabled;
|
||||
|
||||
+2
-2
@@ -25,6 +25,6 @@
|
||||
#define VERSION_MAJOR 3
|
||||
#define VERSION_MINOR 8
|
||||
#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