You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

290 lines
7.8 KiB

#include "rc_internal.h"
#include <stddef.h>
#include <string.h> /* memset */
void rc_parse_trigger_internal(rc_trigger_t* self, const char** memaddr, rc_parse_state_t* parse) {
rc_condset_t** next;
const char* aux;
aux = *memaddr;
next = &self->alternative;
/* reset in case multiple triggers are parsed by the same parse_state */
parse->measured_target = 0;
parse->has_required_hits = 0;
parse->measured_as_percent = 0;
if (*aux == 's' || *aux == 'S') {
self->requirement = 0;
}
else {
self->requirement = rc_parse_condset(&aux, parse, 0);
if (parse->offset < 0) {
return;
}
self->requirement->next = 0;
}
while (*aux == 's' || *aux == 'S') {
aux++;
*next = rc_parse_condset(&aux, parse, 0);
if (parse->offset < 0) {
return;
}
next = &(*next)->next;
}
*next = 0;
*memaddr = aux;
self->measured_value = 0;
self->measured_target = parse->measured_target;
self->measured_as_percent = parse->measured_as_percent;
self->state = RC_TRIGGER_STATE_WAITING;
self->has_hits = 0;
self->has_required_hits = parse->has_required_hits;
}
int rc_trigger_size(const char* memaddr) {
rc_trigger_t* self;
rc_parse_state_t parse;
rc_memref_t* memrefs;
rc_init_parse_state(&parse, 0, 0, 0);
rc_init_parse_state_memrefs(&parse, &memrefs);
self = RC_ALLOC(rc_trigger_t, &parse);
rc_parse_trigger_internal(self, &memaddr, &parse);
rc_destroy_parse_state(&parse);
return parse.offset;
}
rc_trigger_t* rc_parse_trigger(void* buffer, const char* memaddr, lua_State* L, int funcs_ndx) {
rc_trigger_t* self;
rc_parse_state_t parse;
if (!buffer || !memaddr)
return NULL;
rc_init_parse_state(&parse, buffer, L, funcs_ndx);
self = RC_ALLOC(rc_trigger_t, &parse);
rc_init_parse_state_memrefs(&parse, &self->memrefs);
rc_parse_trigger_internal(self, &memaddr, &parse);
rc_destroy_parse_state(&parse);
return (parse.offset >= 0) ? self : NULL;
}
int rc_trigger_state_active(int state)
{
switch (state)
{
case RC_TRIGGER_STATE_DISABLED:
case RC_TRIGGER_STATE_INACTIVE:
case RC_TRIGGER_STATE_TRIGGERED:
return 0;
default:
return 1;
}
}
static int rc_condset_is_measured_from_hitcount(const rc_condset_t* condset, unsigned measured_value)
{
const rc_condition_t* condition;
for (condition = condset->conditions; condition; condition = condition->next) {
if (condition->type == RC_CONDITION_MEASURED && condition->required_hits &&
condition->current_hits == measured_value) {
return 1;
}
}
return 0;
}
static void rc_reset_trigger_hitcounts(rc_trigger_t* self) {
rc_condset_t* condset;
if (self->requirement) {
rc_reset_condset(self->requirement);
}
condset = self->alternative;
while (condset) {
rc_reset_condset(condset);
condset = condset->next;
}
}
int rc_evaluate_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State* L) {
rc_eval_state_t eval_state;
rc_condset_t* condset;
int ret;
char is_paused;
char is_primed;
switch (self->state)
{
case RC_TRIGGER_STATE_TRIGGERED:
/* previously triggered. do nothing - return INACTIVE so caller doesn't think it triggered again */
return RC_TRIGGER_STATE_INACTIVE;
case RC_TRIGGER_STATE_DISABLED:
/* unsupported. do nothing - return INACTIVE */
return RC_TRIGGER_STATE_INACTIVE;
case RC_TRIGGER_STATE_INACTIVE:
/* not yet active. update the memrefs so deltas are correct when it becomes active, then return INACTIVE */
rc_update_memref_values(self->memrefs, peek, ud);
return RC_TRIGGER_STATE_INACTIVE;
default:
break;
}
/* update the memory references */
rc_update_memref_values(self->memrefs, peek, ud);
/* process the trigger */
memset(&eval_state, 0, sizeof(eval_state));
eval_state.peek = peek;
eval_state.peek_userdata = ud;
eval_state.L = L;
if (self->requirement != NULL) {
ret = rc_test_condset(self->requirement, &eval_state);
is_paused = self->requirement->is_paused;
is_primed = eval_state.primed;
} else {
ret = 1;
is_paused = 0;
is_primed = 1;
}
condset = self->alternative;
if (condset) {
int sub = 0;
char sub_paused = 1;
char sub_primed = 0;
do {
sub |= rc_test_condset(condset, &eval_state);
sub_paused &= condset->is_paused;
sub_primed |= eval_state.primed;
condset = condset->next;
} while (condset);
/* to trigger, the core must be true and at least one alt must be true */
ret &= sub;
is_primed &= sub_primed;
/* if the core is not paused, all alts must be paused to count as a paused trigger */
is_paused |= sub_paused;
}
/* if paused, the measured value may not be captured, keep the old value */
if (!is_paused) {
rc_typed_value_convert(&eval_state.measured_value, RC_VALUE_TYPE_UNSIGNED);
self->measured_value = eval_state.measured_value.value.u32;
}
/* if the state is WAITING and the trigger is ready to fire, ignore it and reset the hit counts */
/* otherwise, if the state is WAITING, proceed to activating the trigger */
if (self->state == RC_TRIGGER_STATE_WAITING && ret) {
rc_reset_trigger(self);
self->has_hits = 0;
return RC_TRIGGER_STATE_WAITING;
}
/* if any ResetIf condition was true, reset the hit counts */
if (eval_state.was_reset) {
/* if the measured value came from a hit count, reset it. do this before calling
* rc_reset_trigger_hitcounts in case we need to call rc_condset_is_measured_from_hitcount */
if (eval_state.measured_from_hits) {
self->measured_value = 0;
}
else if (is_paused && self->measured_value) {
/* if the measured value is in a paused group, measured_from_hits won't have been set.
* attempt to determine if it should have been */
if (self->requirement && self->requirement->is_paused &&
rc_condset_is_measured_from_hitcount(self->requirement, self->measured_value)) {
self->measured_value = 0;
}
else {
for (condset = self->alternative; condset; condset = condset->next) {
if (condset->is_paused && rc_condset_is_measured_from_hitcount(condset, self->measured_value)) {
self->measured_value = 0;
break;
}
}
}
}
rc_reset_trigger_hitcounts(self);
/* if there were hit counts to clear, return RESET, but don't change the state */
if (self->has_hits) {
self->has_hits = 0;
/* cannot be PRIMED while ResetIf is true */
if (self->state == RC_TRIGGER_STATE_PRIMED)
self->state = RC_TRIGGER_STATE_ACTIVE;
return RC_TRIGGER_STATE_RESET;
}
/* any hits that were tallied were just reset */
eval_state.has_hits = 0;
is_primed = 0;
}
else if (ret) {
/* trigger was triggered */
self->state = RC_TRIGGER_STATE_TRIGGERED;
return RC_TRIGGER_STATE_TRIGGERED;
}
/* did not trigger this frame - update the information we'll need for next time */
self->has_hits = eval_state.has_hits;
if (is_paused) {
self->state = RC_TRIGGER_STATE_PAUSED;
}
else if (is_primed) {
self->state = RC_TRIGGER_STATE_PRIMED;
}
else {
self->state = RC_TRIGGER_STATE_ACTIVE;
}
/* if an individual condition was reset, notify the caller */
if (eval_state.was_cond_reset)
return RC_TRIGGER_STATE_RESET;
/* otherwise, just return the current state */
return self->state;
}
int rc_test_trigger(rc_trigger_t* self, rc_peek_t peek, void* ud, lua_State* L) {
/* for backwards compatibilty, rc_test_trigger always assumes the achievement is active */
self->state = RC_TRIGGER_STATE_ACTIVE;
return (rc_evaluate_trigger(self, peek, ud, L) == RC_TRIGGER_STATE_TRIGGERED);
}
void rc_reset_trigger(rc_trigger_t* self) {
rc_reset_trigger_hitcounts(self);
self->state = RC_TRIGGER_STATE_WAITING;
self->measured_value = 0;
self->has_hits = 0;
}