|
|
|
@ -32,6 +32,7 @@ distribution.
|
|
|
|
|
#include <cstring>
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <numeric>
|
|
|
|
|
#include <functional>
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
#include "VersionInfo.h"
|
|
|
|
@ -80,6 +81,8 @@ using namespace std;
|
|
|
|
|
#include "df/unit_soul.h"
|
|
|
|
|
#include "df/unit_wound.h"
|
|
|
|
|
#include "df/world.h"
|
|
|
|
|
#include "df/unit_action.h"
|
|
|
|
|
#include "df/unit_action_type_group.h"
|
|
|
|
|
|
|
|
|
|
using namespace DFHack;
|
|
|
|
|
using namespace df::enums;
|
|
|
|
@ -1949,3 +1952,166 @@ int Units::getStressCategoryRaw(int32_t stress_level)
|
|
|
|
|
}
|
|
|
|
|
return level;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int32_t *getActionTimerPointer(df::unit_action *action) {
|
|
|
|
|
switch (action->type)
|
|
|
|
|
{
|
|
|
|
|
case unit_action_type::None:
|
|
|
|
|
break;
|
|
|
|
|
case unit_action_type::Move:
|
|
|
|
|
return &action->data.move.timer;
|
|
|
|
|
case unit_action_type::Attack:
|
|
|
|
|
if (action->data.attack.timer1 != 0) {
|
|
|
|
|
// Wind-up timer is still active, work on it
|
|
|
|
|
return &action->data.attack.timer1;
|
|
|
|
|
} else {
|
|
|
|
|
// Wind-up timer is finished, work on recovery timer
|
|
|
|
|
return &action->data.attack.timer2;
|
|
|
|
|
}
|
|
|
|
|
case unit_action_type::HoldTerrain:
|
|
|
|
|
return &action->data.holdterrain.timer;
|
|
|
|
|
case unit_action_type::Climb:
|
|
|
|
|
return &action->data.climb.timer;
|
|
|
|
|
case unit_action_type::Job:
|
|
|
|
|
return &action->data.job.timer;
|
|
|
|
|
// could also patch the unit->job.current_job->completion_timer
|
|
|
|
|
case unit_action_type::Talk:
|
|
|
|
|
return &action->data.talk.timer;
|
|
|
|
|
case unit_action_type::Unsteady:
|
|
|
|
|
return &action->data.unsteady.timer;
|
|
|
|
|
case unit_action_type::Dodge:
|
|
|
|
|
return &action->data.dodge.timer;
|
|
|
|
|
case unit_action_type::Recover:
|
|
|
|
|
return &action->data.recover.timer;
|
|
|
|
|
case unit_action_type::StandUp:
|
|
|
|
|
return &action->data.standup.timer;
|
|
|
|
|
case unit_action_type::LieDown:
|
|
|
|
|
return &action->data.liedown.timer;
|
|
|
|
|
case unit_action_type::Job2:
|
|
|
|
|
return &action->data.job2.timer;
|
|
|
|
|
// could also patch the unit->job.current_job->completion_timer
|
|
|
|
|
case unit_action_type::PushObject:
|
|
|
|
|
return &action->data.pushobject.timer;
|
|
|
|
|
case unit_action_type::SuckBlood:
|
|
|
|
|
return &action->data.suckblood.timer;
|
|
|
|
|
case unit_action_type::Jump:
|
|
|
|
|
case unit_action_type::ReleaseTerrain:
|
|
|
|
|
case unit_action_type::Parry:
|
|
|
|
|
case unit_action_type::Block:
|
|
|
|
|
case unit_action_type::HoldItem:
|
|
|
|
|
case unit_action_type::ReleaseItem:
|
|
|
|
|
case unit_action_type::Unk20:
|
|
|
|
|
case unit_action_type::Unk21:
|
|
|
|
|
case unit_action_type::Unk22:
|
|
|
|
|
case unit_action_type::Unk23:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void mutateActionTimerCore(df::unit_action *action, std::function<double(double)> mutator) {
|
|
|
|
|
int32_t *timer = getActionTimerPointer(action);
|
|
|
|
|
if (timer != nullptr && *timer > 0) {
|
|
|
|
|
double value = *timer;
|
|
|
|
|
value = mutator(value);
|
|
|
|
|
if (value > INT32_MAX) {
|
|
|
|
|
value = INT32_MAX;
|
|
|
|
|
} else if (value < INT32_MIN) {
|
|
|
|
|
value = INT32_MIN;
|
|
|
|
|
}
|
|
|
|
|
*timer = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Units::subtractActionTimers(color_ostream &out, df::unit *unit, int32_t amount, df::unit_action_type affectedActionType)
|
|
|
|
|
{
|
|
|
|
|
CHECK_NULL_POINTER(unit);
|
|
|
|
|
for (auto action : unit->actions) {
|
|
|
|
|
if (affectedActionType != action->type) continue;
|
|
|
|
|
mutateActionTimerCore(action, [=](double timerValue){return max(timerValue - amount, 1.0);});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Units::subtractGroupActionTimers(color_ostream &out, df::unit *unit, int32_t amount, df::unit_action_type_group affectedActionTypeGroup)
|
|
|
|
|
{
|
|
|
|
|
CHECK_NULL_POINTER(unit);
|
|
|
|
|
for (auto action : unit->actions) {
|
|
|
|
|
auto list = ENUM_ATTR(unit_action_type, group, action->type);
|
|
|
|
|
for (size_t i = 0; i < list.size; i++) {
|
|
|
|
|
if (list.items[i] == affectedActionTypeGroup) {
|
|
|
|
|
mutateActionTimerCore(action, [=](double timerValue){return max(timerValue - amount, 1.0);});
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool validateMultiplyActionTimerAmount(color_ostream &out, float amount) {
|
|
|
|
|
if (amount < 0) {
|
|
|
|
|
out.printerr("Can't multiply action timer(s) by negative amount.\n");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Units::multiplyActionTimers(color_ostream &out, df::unit *unit, float amount, df::unit_action_type affectedActionType)
|
|
|
|
|
{
|
|
|
|
|
CHECK_NULL_POINTER(unit);
|
|
|
|
|
if (!validateMultiplyActionTimerAmount(out, amount))
|
|
|
|
|
return;
|
|
|
|
|
for (auto action : unit->actions) {
|
|
|
|
|
if (affectedActionType != action->type) continue;
|
|
|
|
|
mutateActionTimerCore(action, [=](double timerValue){return max(timerValue * amount, 1.0);});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Units::multiplyGroupActionTimers(color_ostream &out, df::unit *unit, float amount, df::unit_action_type_group affectedActionTypeGroup)
|
|
|
|
|
{
|
|
|
|
|
CHECK_NULL_POINTER(unit);
|
|
|
|
|
if (!validateMultiplyActionTimerAmount(out, amount))
|
|
|
|
|
return;
|
|
|
|
|
for (auto action : unit->actions) {
|
|
|
|
|
auto list = ENUM_ATTR(unit_action_type, group, action->type);
|
|
|
|
|
for (size_t i = 0; i < list.size; i++) {
|
|
|
|
|
if (list.items[i] == affectedActionTypeGroup) {
|
|
|
|
|
mutateActionTimerCore(action, [=](double timerValue){return max(timerValue * amount, 1.0);});
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool validateSetActionTimerAmount(color_ostream &out, int32_t amount) {
|
|
|
|
|
if (amount <= 0) {
|
|
|
|
|
out.printerr("Can't set action timer(s) to non-positive amount.\n");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Units::setActionTimers(color_ostream &out, df::unit *unit, int32_t amount, df::unit_action_type affectedActionType)
|
|
|
|
|
{
|
|
|
|
|
CHECK_NULL_POINTER(unit);
|
|
|
|
|
if (!validateSetActionTimerAmount(out, amount))
|
|
|
|
|
return;
|
|
|
|
|
for (auto action : unit->actions) {
|
|
|
|
|
if (affectedActionType != action->type) continue;
|
|
|
|
|
mutateActionTimerCore(action, [=](double timerValue){return amount;});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Units::setGroupActionTimers(color_ostream &out, df::unit *unit, int32_t amount, df::unit_action_type_group affectedActionTypeGroup)
|
|
|
|
|
{
|
|
|
|
|
CHECK_NULL_POINTER(unit);
|
|
|
|
|
if (!validateSetActionTimerAmount(out, amount))
|
|
|
|
|
return;
|
|
|
|
|
for (auto action : unit->actions) {
|
|
|
|
|
auto list = ENUM_ATTR(unit_action_type, group, action->type);
|
|
|
|
|
for (size_t i = 0; i < list.size; i++) {
|
|
|
|
|
if (list.items[i] == affectedActionTypeGroup) {
|
|
|
|
|
mutateActionTimerCore(action, [=](double timerValue){return amount;});
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|