New RNG API, with crude semi-automatic misuse detection
This commit is contained in:
parent
753858b968
commit
1f1db18076
11 changed files with 487 additions and 178 deletions
|
@ -14,7 +14,8 @@ class TaiseiError(RuntimeError):
|
|||
class DefaultArgs(object):
|
||||
def __init__(self):
|
||||
self.fallback_version = None
|
||||
self.rootdir = Path(__file__).parent.parent.parent
|
||||
self.rootdir = Path(os.environ.get('MESON_SOURCE_ROOT', Path(__file__).parent.parent.parent)).resolve()
|
||||
self.builddir = Path(os.environ.get('MESON_BUILD_ROOT', '.')).resolve()
|
||||
self.depfile = None
|
||||
|
||||
|
||||
|
@ -33,6 +34,12 @@ def add_common_args(parser, *, depfile=False):
|
|||
help='Taisei source directory'
|
||||
)
|
||||
|
||||
parser.add_argument('--builddir',
|
||||
type=Path,
|
||||
default=default_args.builddir,
|
||||
help='Taisei build directory'
|
||||
)
|
||||
|
||||
if depfile:
|
||||
parser.add_argument('--depfile',
|
||||
type=Path,
|
||||
|
@ -62,15 +69,15 @@ def run_main(func, args=None):
|
|||
|
||||
def write_depfile(depfile, target, deps):
|
||||
with Path(depfile).open('w') as df:
|
||||
l = [str(target) + ":"] + list(str(d) for d in deps) + [str(Path(__file__).resolve())]
|
||||
df.write(" \\\n ".join(l))
|
||||
l = [str(target) + ':'] + list(str(d) for d in deps) + [str(Path(__file__).resolve())]
|
||||
df.write(' \\\n '.join(l))
|
||||
|
||||
|
||||
def update_text_file(outpath, data):
|
||||
import io
|
||||
|
||||
try:
|
||||
with open(str(outpath), "r+t") as outfile:
|
||||
with open(str(outpath), 'r+t') as outfile:
|
||||
contents = outfile.read()
|
||||
|
||||
if contents == data:
|
||||
|
@ -80,7 +87,7 @@ def update_text_file(outpath, data):
|
|||
outfile.write(data)
|
||||
outfile.truncate()
|
||||
except (FileNotFoundError, io.UnsupportedOperation):
|
||||
with open(str(outpath), "w") as outfile:
|
||||
with open(str(outpath), 'w') as outfile:
|
||||
outfile.write(data)
|
||||
|
||||
|
||||
|
|
|
@ -26,12 +26,21 @@ def main(args):
|
|||
scripts = pargs.rootdir / 'scripts' / 'upkeep'
|
||||
|
||||
tasks = (
|
||||
'fixup-source-files',
|
||||
['fixup-source-files', 'check-rng-usage'],
|
||||
'update-glsl-sources',
|
||||
)
|
||||
|
||||
with ThreadPoolExecutor() as ex:
|
||||
tuple(ex.map(lambda task: subprocess.check_call([scripts / f'{task}.py'] + args[1:]), tasks))
|
||||
def do_task(task):
|
||||
if isinstance(task, str):
|
||||
print('[upkeep] begin task', task)
|
||||
subprocess.check_call([scripts / f'{task}.py'] + args[1:])
|
||||
print('[upkeep] task', task, 'done')
|
||||
else:
|
||||
for t in task:
|
||||
do_task(t)
|
||||
|
||||
tuple(ex.map(do_task, tasks))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
106
scripts/upkeep/check-rng-usage.py
Executable file
106
scripts/upkeep/check-rng-usage.py
Executable file
|
@ -0,0 +1,106 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import re
|
||||
import ast
|
||||
import json
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import code
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from taiseilib.common import (
|
||||
run_main,
|
||||
add_common_args,
|
||||
)
|
||||
|
||||
from concurrent.futures import (
|
||||
ThreadPoolExecutor,
|
||||
)
|
||||
|
||||
|
||||
re_suspicious_rng = re.compile(r'rng_next\(\)[^;]+?rng_next\(\)', re.DOTALL)
|
||||
re_fileinfo = re.compile(r'\n# +(\d+) +(".*?").*?\n')
|
||||
|
||||
|
||||
def get_source_info(m):
|
||||
src = m.string[:m.end()]
|
||||
fileinfo_index = src.rindex('\n# ')
|
||||
src_since_anchor = src[fileinfo_index:]
|
||||
|
||||
lineno, fname = re_fileinfo.search(src_since_anchor).groups()
|
||||
lineno = int(lineno)
|
||||
lineno += src_since_anchor.count('\n')
|
||||
fname = ast.literal_eval(fname)
|
||||
|
||||
return (fname, lineno)
|
||||
|
||||
|
||||
def expand_line(m):
|
||||
src = m.string
|
||||
start, end = m.span()
|
||||
|
||||
while src[start] != '\n':
|
||||
start -= 1
|
||||
|
||||
while src[end] != '\n':
|
||||
end += 1
|
||||
|
||||
return src[start:m.start()] + '\x1b[1;31m' + src[m.start():m.end()] + '\x1b[0m' + src[m.end():end]
|
||||
|
||||
|
||||
def find_suspicious_callsites(src):
|
||||
for m in re_suspicious_rng.finditer(src):
|
||||
fname, lineno = get_source_info(m)
|
||||
segment = expand_line(m)
|
||||
print(f'{fname}:{lineno}: Suspected RNG API misuse: {segment}\n', file=sys.stderr)
|
||||
|
||||
|
||||
def remove_flag(cmd, flag, nvalues=0):
|
||||
while True:
|
||||
try:
|
||||
i = cmd.index(flag)
|
||||
for n in range(1 + nvalues):
|
||||
del cmd[i]
|
||||
except ValueError:
|
||||
break
|
||||
|
||||
|
||||
def preprocess(cmd_json):
|
||||
cmd = cmd_json['command']
|
||||
cmd = shlex.split(cmd)
|
||||
|
||||
remove_flag(cmd, '-o')
|
||||
remove_flag(cmd, '-c')
|
||||
remove_flag(cmd, '-include', 1)
|
||||
remove_flag(cmd, '-fpch-preprocess')
|
||||
remove_flag(cmd, '-MD')
|
||||
remove_flag(cmd, '-MQ', 1)
|
||||
remove_flag(cmd, '-MF', 1)
|
||||
remove_flag(cmd, cmd_json['output'])
|
||||
|
||||
cmd += ['-DRNG_API_CHECK', '-E', '-o', '-']
|
||||
|
||||
# print(' '.join(cmd))
|
||||
p = subprocess.run(cmd, stdout=subprocess.PIPE, cwd=cmd_json['directory'])
|
||||
|
||||
return p.stdout.decode('utf8')
|
||||
|
||||
|
||||
def main(args):
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description='Update build defintions to include all GLSL files.', prog=args[0])
|
||||
add_common_args(parser)
|
||||
|
||||
args = parser.parse_args(args[1:])
|
||||
|
||||
with (args.builddir / 'compile_commands.json').open() as f:
|
||||
compile_commands = json.load(f)
|
||||
|
||||
with ThreadPoolExecutor() as ex:
|
||||
tuple(ex.map(lambda c: find_suspicious_callsites(preprocess(c)), compile_commands))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run_main(main)
|
|
@ -55,7 +55,7 @@ complex common_wander(complex origin, double dist, Rect bounds) {
|
|||
// assert(point_in_rect(origin, bounds));
|
||||
|
||||
while(attempts--) {
|
||||
angle = rand_angle();
|
||||
angle = rng_angle();
|
||||
dir = cdir(angle);
|
||||
dest = origin + dist * dir;
|
||||
|
||||
|
|
|
@ -15,9 +15,9 @@ Global global;
|
|||
void init_global(CLIAction *cli) {
|
||||
memset(&global, 0, sizeof(global));
|
||||
|
||||
tsrand_init(&global.rand_game, time(0));
|
||||
tsrand_init(&global.rand_visual, time(0));
|
||||
tsrand_switch(&global.rand_visual);
|
||||
rng_init(&global.rand_game, time(0));
|
||||
rng_init(&global.rand_visual, time(0));
|
||||
rng_make_active(&global.rand_visual);
|
||||
|
||||
memset(&global.replay, 0, sizeof(Replay));
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include "move.h"
|
||||
#include "coroutine.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#if defined(DEBUG) && !defined(RNG_API_CHECK)
|
||||
#define PROJ_DEBUG
|
||||
#endif
|
||||
|
||||
|
|
215
src/random.c
215
src/random.c
|
@ -8,12 +8,12 @@
|
|||
|
||||
#include "taisei.h"
|
||||
|
||||
#include "random.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "global.h"
|
||||
#include "random.h"
|
||||
|
||||
static RandomState *tsrand_current;
|
||||
static RandomState *rng_active_state;
|
||||
|
||||
uint64_t splitmix64(uint64_t *state) {
|
||||
// from http://xoshiro.di.unimi.it/splitmix64.c
|
||||
|
@ -25,7 +25,7 @@ uint64_t splitmix64(uint64_t *state) {
|
|||
}
|
||||
|
||||
uint64_t makeseed(void) {
|
||||
static uint64_t s;
|
||||
static uint64_t s = (uintptr_t)&makeseed;
|
||||
return splitmix64(&s) ^ SDL_GetPerformanceCounter();
|
||||
}
|
||||
|
||||
|
@ -49,85 +49,166 @@ static uint64_t xoshiro256plus(uint64_t s[4]) {
|
|||
return result_plus;
|
||||
}
|
||||
|
||||
uint32_t tsrand_p(RandomState *rnd) {
|
||||
assert(!rnd->locked);
|
||||
return xoshiro256plus(rnd->state) >> 32;
|
||||
void rng_init(RandomState *rng, uint64_t seed) {
|
||||
memset(rng, 0, sizeof(*rng));
|
||||
rng_seed(rng, seed);
|
||||
}
|
||||
|
||||
uint64_t tsrand64_p(RandomState *rnd) {
|
||||
assert(!rnd->locked);
|
||||
return xoshiro256plus(rnd->state);
|
||||
void rng_seed(RandomState *rng, uint64_t seed) {
|
||||
rng->state[0] = splitmix64(&seed);
|
||||
rng->state[1] = splitmix64(&seed);
|
||||
rng->state[2] = splitmix64(&seed);
|
||||
rng->state[3] = splitmix64(&seed);
|
||||
}
|
||||
|
||||
void tsrand_seed_p(RandomState *rnd, uint64_t seed) {
|
||||
rnd->state[0] = splitmix64(&seed);
|
||||
rnd->state[1] = splitmix64(&seed);
|
||||
rnd->state[2] = splitmix64(&seed);
|
||||
rnd->state[3] = splitmix64(&seed);
|
||||
void rng_make_active(RandomState *rng) {
|
||||
rng_active_state = rng;
|
||||
}
|
||||
|
||||
void tsrand_switch(RandomState *rnd) {
|
||||
tsrand_current = rnd;
|
||||
rng_val_t rng_next_p(RandomState *rng) {
|
||||
assert(!rng->locked);
|
||||
return (rng_val_t) { xoshiro256plus(rng->state) };
|
||||
}
|
||||
|
||||
void tsrand_init(RandomState *rnd, uint64_t seed) {
|
||||
memset(rnd, 0, sizeof(RandomState));
|
||||
tsrand_seed_p(rnd, seed);
|
||||
rng_val_t rng_next(void) {
|
||||
return rng_next_p(rng_active_state);
|
||||
}
|
||||
|
||||
void tsrand_seed(uint64_t seed) {
|
||||
tsrand_seed_p(tsrand_current, seed);
|
||||
void rng_nextn(size_t n, rng_val_t v[n]) {
|
||||
for(size_t i = 0; i < n; ++i) {
|
||||
v[i] = rng_next();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t tsrand(void) {
|
||||
return tsrand_p(tsrand_current);
|
||||
/*
|
||||
* Output conversion functions
|
||||
*/
|
||||
|
||||
uint64_t vrng_u64(rng_val_t v) {
|
||||
return v._value;
|
||||
}
|
||||
|
||||
uint64_t tsrand64(void) {
|
||||
return tsrand64_p(tsrand_current);
|
||||
int64_t vrng_i64(rng_val_t v) {
|
||||
return (int64_t)v._value;
|
||||
}
|
||||
|
||||
static inline double make_unsigned_double(uint64_t x) {
|
||||
// Range: [0.0; 1.0)
|
||||
return (x >> 11) * 0x1.0p-53;
|
||||
uint32_t vrng_u32(rng_val_t v) {
|
||||
return v._value >> 32;
|
||||
}
|
||||
|
||||
static inline double make_signed_double(uint64_t x) {
|
||||
// Range: (-1.0; 1.0)
|
||||
// NOTE: slight bias towards 0, because -0 exists
|
||||
int32_t vrng_i32(rng_val_t v) {
|
||||
return (int32_t)(v._value >> 32);
|
||||
}
|
||||
|
||||
double vrng_f64(rng_val_t v) {
|
||||
return (v._value >> 11) * 0x1.0p-53;
|
||||
}
|
||||
|
||||
double vrng_f64s(rng_val_t v) {
|
||||
DoubleBits db;
|
||||
db.val = make_unsigned_double(x << 1);
|
||||
db.bits |= x & (UINT64_C(1) << 63);
|
||||
db.val = vrng_f64((rng_val_t) { v._value << 1 });
|
||||
db.bits |= v._value & (UINT64_C(1) << 63);
|
||||
return db.val;
|
||||
}
|
||||
|
||||
float vrng_f32(rng_val_t v) {
|
||||
return (v._value >> 40) * 0x1.0p-24f;
|
||||
}
|
||||
|
||||
float vrng_f32s(rng_val_t v) {
|
||||
FloatBits fb;
|
||||
fb.val = vrng_f32((rng_val_t) { v._value << 1 });
|
||||
fb.bits |= vrng_u32(v) & (1 << 31);
|
||||
return fb.val;
|
||||
}
|
||||
|
||||
bool vrng_bool(rng_val_t v) {
|
||||
return v._value >> 63;
|
||||
}
|
||||
|
||||
double vrng_f64_sign(rng_val_t v) {
|
||||
return bits_to_double((UINT64_C(0x3FF) << 52) | (v._value & (UINT64_C(1) << 63)));
|
||||
}
|
||||
|
||||
float vrng_f32_sign(rng_val_t v) {
|
||||
return bits_to_float((0x7f << 23) | (vrng_u32(v) & (1 << 31)));
|
||||
}
|
||||
|
||||
double vrng_f64_range(rng_val_t v, double rmin, double rmax) {
|
||||
return vrng_f64(v) * (rmax - rmin) + rmin;
|
||||
}
|
||||
|
||||
float vrng_f32_range(rng_val_t v, float rmin, float rmax) {
|
||||
return vrng_f32(v) * (rmax - rmin) + rmin;
|
||||
}
|
||||
|
||||
int64_t vrng_i64_range(rng_val_t v, int64_t rmin, int64_t rmax) {
|
||||
// NOTE: strictly speaking non-uniform distribution in the general case, but seems good enough for small numbers.
|
||||
int64_t period = rmax - rmin;
|
||||
assume(period > 0);
|
||||
return (int64_t)(v._value % (uint64_t)period) + rmin;
|
||||
}
|
||||
|
||||
int32_t vrng_i32_range(rng_val_t v, int32_t rmin, int32_t rmax) {
|
||||
// NOTE: strictly speaking non-uniform distribution in the general case, but seems good enough for small numbers.
|
||||
int32_t period = rmax - rmin;
|
||||
assume(period > 0);
|
||||
return (int32_t)(vrng_u32(v) % (uint32_t)period) + rmin;
|
||||
}
|
||||
|
||||
double vrng_f64_angle(rng_val_t v) {
|
||||
return vrng_f64(v) * (M_PI * 2.0);
|
||||
}
|
||||
|
||||
float vrng_f32_angle(rng_val_t v) {
|
||||
return vrng_f32(v) * (float)(M_PI * 2.0f);
|
||||
}
|
||||
|
||||
complex vrng_dir(rng_val_t v) {
|
||||
return cdir(vrng_f64_angle(v));
|
||||
}
|
||||
|
||||
bool vrng_f64_chance(rng_val_t v, double chance) {
|
||||
return vrng_f64(v) < chance;
|
||||
}
|
||||
|
||||
bool vrng_f32_chance(rng_val_t v, float chance) {
|
||||
return vrng_f32(v) < chance;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deprecated APIs; to be removed
|
||||
*/
|
||||
|
||||
uint32_t tsrand_p(RandomState *rng) {
|
||||
return vrng_u32(rng_next_p(rng));
|
||||
}
|
||||
|
||||
uint64_t tsrand64_p(RandomState *rng) {
|
||||
return vrng_u64(rng_next_p(rng));
|
||||
}
|
||||
|
||||
uint32_t tsrand(void) {
|
||||
return rng_u32();
|
||||
}
|
||||
|
||||
uint64_t tsrand64(void) {
|
||||
return rng_u64();
|
||||
}
|
||||
|
||||
double frand(void) {
|
||||
return make_unsigned_double(tsrand64());
|
||||
return rng_f64();
|
||||
}
|
||||
|
||||
double nfrand(void) {
|
||||
return make_signed_double(tsrand64());
|
||||
return rng_f64s();
|
||||
}
|
||||
|
||||
bool rand_bool(void) {
|
||||
return tsrand64() >> 63;
|
||||
}
|
||||
|
||||
double rand_sign(void) {
|
||||
return bits_to_double((UINT64_C(0x3FF) << 52) | (tsrand64() & (UINT64_C(1) << 63)));
|
||||
}
|
||||
|
||||
float rand_signf(void) {
|
||||
return bits_to_float((0x7f << 23) | (tsrand() & (1 << 31)));
|
||||
}
|
||||
|
||||
// we use this to support multiple rands in a single statement without breaking replays across different builds
|
||||
|
||||
static uint64_t tsrand_array[TSRAND_ARRAY_LIMIT];
|
||||
static rng_val_t tsrand_array[TSRAND_ARRAY_LIMIT];
|
||||
static int tsrand_array_elems;
|
||||
static uint64_t tsrand_fillflags = 0;
|
||||
|
||||
static void tsrand_error(const char *file, const char *func, uint line, const char *fmt, ...) {
|
||||
noreturn static void tsrand_error(const char *file, const char *func, uint line, const char *fmt, ...) {
|
||||
char buf[2048] = { 0 };
|
||||
va_list args;
|
||||
|
||||
|
@ -136,6 +217,7 @@ static void tsrand_error(const char *file, const char *func, uint line, const ch
|
|||
va_end(args);
|
||||
|
||||
log_fatal("%s(): %s [%s:%u]", func, buf, file, line);
|
||||
UNREACHABLE;
|
||||
}
|
||||
|
||||
#define TSRANDERR(...) tsrand_error(file, __func__, line, __VA_ARGS__)
|
||||
|
@ -150,18 +232,17 @@ void _tsrand_fill_p(RandomState *rnd, int amount, const char *file, uint line) {
|
|||
tsrand_fillflags = (UINT64_C(1) << amount) - 1;
|
||||
|
||||
for(int i = 0; i < amount; ++i) {
|
||||
tsrand_array[i] = tsrand64_p(rnd);
|
||||
tsrand_array[i] = rng_next_p(rnd);
|
||||
}
|
||||
}
|
||||
|
||||
void _tsrand_fill(int amount, const char *file, uint line) {
|
||||
_tsrand_fill_p(tsrand_current, amount, file, line);
|
||||
_tsrand_fill_p(rng_active_state, amount, file, line);
|
||||
}
|
||||
|
||||
uint64_t _tsrand64_a(int idx, const char *file, uint line) {
|
||||
static rng_val_t _tsrand_val_a(int idx, const char *file, uint line) {
|
||||
if(idx >= tsrand_array_elems || idx < 0) {
|
||||
TSRANDERR("Index out of range (%i / %i)", idx, tsrand_array_elems);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(tsrand_fillflags & (UINT64_C(1) << idx)) {
|
||||
|
@ -170,25 +251,21 @@ uint64_t _tsrand64_a(int idx, const char *file, uint line) {
|
|||
}
|
||||
|
||||
TSRANDERR("Index %i used multiple times", idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
uint64_t _tsrand64_a(int idx, const char *file, uint line) {
|
||||
return vrng_u64(_tsrand_val_a(idx, file, line));
|
||||
}
|
||||
|
||||
uint32_t _tsrand_a(int idx, const char *file, uint line) {
|
||||
return _tsrand64_a(idx, file, line) >> 32;
|
||||
return vrng_u32(_tsrand_val_a(idx, file, line));
|
||||
}
|
||||
|
||||
double _afrand(int idx, const char *file, uint line) {
|
||||
return make_unsigned_double(_tsrand64_a(idx, file, line));
|
||||
return vrng_f64(_tsrand_val_a(idx, file, line));
|
||||
}
|
||||
|
||||
double _anfrand(int idx, const char *file, uint line) {
|
||||
return make_signed_double(_tsrand64_a(idx, file, line));
|
||||
}
|
||||
|
||||
void tsrand_lock(RandomState *rnd) {
|
||||
rnd->locked = true;
|
||||
}
|
||||
|
||||
void tsrand_unlock(RandomState *rnd) {
|
||||
rnd->locked = false;
|
||||
return vrng_f64s(_tsrand_val_a(idx, file, line));
|
||||
}
|
||||
|
|
175
src/random.h
175
src/random.h
|
@ -11,39 +11,155 @@
|
|||
|
||||
#include "taisei.h"
|
||||
|
||||
#include "util/crap.h"
|
||||
#include "util/miscmath.h"
|
||||
|
||||
#define RNG_DEPRECATED attr_deprecated("Use the new rng_ API")
|
||||
|
||||
typedef struct RandomState {
|
||||
uint64_t state[4];
|
||||
#ifdef DEBUG
|
||||
bool locked;
|
||||
#endif
|
||||
} RandomState;
|
||||
|
||||
uint64_t splitmix64(uint64_t *state);
|
||||
typedef struct rng_val {
|
||||
uint64_t _value;
|
||||
} rng_val_t;
|
||||
|
||||
uint64_t splitmix64(uint64_t *state) attr_nonnull(1);
|
||||
uint64_t makeseed(void);
|
||||
|
||||
void tsrand_init(RandomState *rnd, uint64_t seed);
|
||||
void tsrand_switch(RandomState *rnd);
|
||||
void tsrand_seed_p(RandomState *rnd, uint64_t seed);
|
||||
uint32_t tsrand_p(RandomState *rnd);
|
||||
uint64_t tsrand64_p(RandomState *rnd);
|
||||
void rng_init(RandomState *rng, uint64_t seed) attr_nonnull(1);
|
||||
void rng_seed(RandomState *rng, uint64_t seed) attr_nonnull(1);
|
||||
void rng_make_active(RandomState *rng) attr_nonnull(1);
|
||||
rng_val_t rng_next_p(RandomState *rng) attr_nonnull(1);
|
||||
|
||||
void tsrand_seed(uint64_t seed);
|
||||
uint32_t tsrand(void);
|
||||
uint64_t tsrand64(void);
|
||||
#ifdef DEBUG
|
||||
INLINE void rng_lock(RandomState *rng) { rng->locked = true; }
|
||||
INLINE void rng_unlock(RandomState *rng) { rng->locked = false; }
|
||||
#else
|
||||
#define rng_lock(rng) (rng, (void)0)
|
||||
#define rng_unlock(rng) (rng, (void)0)
|
||||
#endif
|
||||
|
||||
void tsrand_lock(RandomState *rnd);
|
||||
void tsrand_unlock(RandomState *rnd);
|
||||
// NOTE: if you rename this, also update scripts/upkeep/check-rng-usage.py!
|
||||
rng_val_t rng_next(void);
|
||||
void rng_nextn(size_t n, rng_val_t v[n]) attr_nonnull(2);
|
||||
|
||||
double frand(void); // Range: [0.0; 1.0)
|
||||
double nfrand(void); // Range: (-1.0; 1.0)
|
||||
bool rand_bool(void);
|
||||
double rand_sign(void); // 1.0 or -1.0
|
||||
float rand_signf(void); // 1.0f or -1.0f
|
||||
#define RNG_ARRAY(name, size) rng_val_t name[size]; RNG_NEXT(name)
|
||||
|
||||
void _tsrand_fill_p(RandomState *rnd, int amount, const char *file, uint line);
|
||||
void _tsrand_fill(int amount, const char *file, uint line);
|
||||
uint32_t _tsrand_a(int idx, const char *file, uint line);
|
||||
uint64_t _tsrand64_a(int idx, const char *file, uint line);
|
||||
double _afrand(int idx, const char *file, uint line);
|
||||
double _anfrand(int idx, const char *file, uint line);
|
||||
#define RNG_NEXT(_val) \
|
||||
rng_nextn(sizeof(_val)/sizeof(rng_val_t), _Generic((_val), \
|
||||
rng_val_t: &(_val), \
|
||||
rng_val_t*: (_val) \
|
||||
))
|
||||
|
||||
uint64_t vrng_u64(rng_val_t v) attr_pure;
|
||||
#define rng_u64() vrng_u64(rng_next())
|
||||
int64_t vrng_i64(rng_val_t v) attr_pure;
|
||||
#define rng_i64() vrng_i64(rng_next())
|
||||
|
||||
uint32_t vrng_u32(rng_val_t v) attr_pure;
|
||||
#define rng_u32() vrng_u32(rng_next())
|
||||
int32_t vrng_i32(rng_val_t v) attr_pure;
|
||||
#define rng_i32() vrng_i32(rng_next())
|
||||
|
||||
double vrng_f64(rng_val_t v) attr_pure;
|
||||
#define rng_f64() vrng_f64(rng_next())
|
||||
double vrng_f64s(rng_val_t v) attr_pure;
|
||||
#define rng_f64s() vrng_f64s(rng_next())
|
||||
|
||||
float vrng_f32(rng_val_t v) attr_pure;
|
||||
#define rng_f32() vrng_f32(rng_next())
|
||||
float vrng_f32s(rng_val_t v) attr_pure;
|
||||
#define rng_f32s() vrng_f32s(rng_next())
|
||||
|
||||
#define vrng_real(v) vrng_f64(v)
|
||||
#define rng_real() vrng_real(rng_next())
|
||||
#define vrng_sreal(v) vrng_f64s(v)
|
||||
#define rng_sreal() vrng_sreal(rng_next())
|
||||
|
||||
bool vrng_bool(rng_val_t v);
|
||||
#define rng_bool() vrng_bool(rng_next())
|
||||
|
||||
double vrng_f64_sign(rng_val_t v) attr_pure;
|
||||
#define rng_f64_sign() vrng_f64_sign(rng_next())
|
||||
float vrng_f32_sign(rng_val_t v) attr_pure;
|
||||
#define rng_f32_sign() vrng_f32_sign(rng_next())
|
||||
|
||||
#define vrng_sign(v) vrng_f64_sign(v)
|
||||
#define rng_sign() vrng_sign(rng_next())
|
||||
|
||||
double vrng_f64_range(rng_val_t v, double rmin, double rmax) attr_pure;
|
||||
#define rng_f64_range(rmin, rmax) vrng_f64_range(rng_next(), rmin, rmax)
|
||||
float vrng_f32_range(rng_val_t v, float rmin, float rmax) attr_pure;
|
||||
#define rng_f32_range(rmin, rmax) vrng_f32_range(rng_next(), rmin, rmax)
|
||||
|
||||
#define vrng_range(v, rmin, rmax) _Generic((rmin), \
|
||||
float: \
|
||||
_Generic((rmax), \
|
||||
float: vrng_f32_range, \
|
||||
default: vrng_f64_range \
|
||||
), \
|
||||
default: vrng_f64_range \
|
||||
)(v, rmin, rmax)
|
||||
|
||||
#define rng_range(rmin, rmax) vrng_range(rng_next(), rmin, rmax)
|
||||
|
||||
int64_t vrng_i64_range(rng_val_t v, int64_t rmin, int64_t rmax) attr_pure;
|
||||
#define rng_i64_range(rmin, rmax) vrng_i64_range(rng_next(), rmin, rmax)
|
||||
int32_t vrng_i32_range(rng_val_t v, int32_t rmin, int32_t rmax) attr_pure;
|
||||
#define rng_i32_range(rmin, rmax) vrng_i32_range(rng_next(), rmin, rmax)
|
||||
|
||||
#define vrng_irange(v, rmin, rmax) vrng_i32_range(v, rmin, rmax)
|
||||
#define rng_irange(rmin, rmax) vrng_range(rng_next(), rmin, rmax)
|
||||
|
||||
double vrng_f64_angle(rng_val_t v) attr_pure;
|
||||
#define rng_f64_angle() vrng_f64_angle(rng_next())
|
||||
float vrng_f32_angle(rng_val_t v) attr_pure;
|
||||
#define rng_f32_angle() vrng_f32_angle(rng_next())
|
||||
|
||||
#define vrng_angle(v) vrng_f64_angle(v)
|
||||
#define rng_angle() vrng_angle(rng_next())
|
||||
|
||||
complex vrng_dir(rng_val_t v) attr_pure;
|
||||
#define rng_dir() vrng_dir(rng_next())
|
||||
|
||||
bool vrng_f64_chance(rng_val_t v, double chance) attr_pure;
|
||||
#define rng_f64_chance(chance) vrng_f64_chance(rng_next(), chance)
|
||||
bool vrng_f32_chance(rng_val_t v, float chance) attr_pure;
|
||||
#define rng_f32_chance(chance) vrng_f32_chance(rng_next(), chance)
|
||||
|
||||
#define vrng_chance(v, chance) _Generic((chance), \
|
||||
float: vrng_f32_chance, \
|
||||
default: vrng_f64_chance \
|
||||
)(v, chance)
|
||||
|
||||
#define rng_chance(chance) vrng_chance(rng_next(), chance)
|
||||
|
||||
/*
|
||||
* Deprecated APIs; to be removed
|
||||
*/
|
||||
|
||||
uint32_t tsrand_p(RandomState *rng) RNG_DEPRECATED;
|
||||
uint64_t tsrand64_p(RandomState *rng) RNG_DEPRECATED;
|
||||
|
||||
uint32_t tsrand(void) RNG_DEPRECATED;
|
||||
uint64_t tsrand64(void) RNG_DEPRECATED;
|
||||
|
||||
double frand(void) RNG_DEPRECATED; // Range: [0.0; 1.0)
|
||||
double nfrand(void) RNG_DEPRECATED; // Range: (-1.0; 1.0)
|
||||
bool rand_bool(void) RNG_DEPRECATED;
|
||||
double rand_sign(void) RNG_DEPRECATED; // 1.0 or -1.0
|
||||
float rand_signf(void) RNG_DEPRECATED; // 1.0f or -1.0f
|
||||
|
||||
void _tsrand_fill_p(RandomState *rnd, int amount, const char *file, uint line) RNG_DEPRECATED;
|
||||
void _tsrand_fill(int amount, const char *file, uint line) RNG_DEPRECATED;
|
||||
uint32_t _tsrand_a(int idx, const char *file, uint line) RNG_DEPRECATED;
|
||||
uint64_t _tsrand64_a(int idx, const char *file, uint line) RNG_DEPRECATED;
|
||||
double _afrand(int idx, const char *file, uint line) RNG_DEPRECATED;
|
||||
double _anfrand(int idx, const char *file, uint line) RNG_DEPRECATED;
|
||||
|
||||
#define tsrand_fill_p(rnd,amount) _tsrand_fill_p(rnd, amount, __FILE__, __LINE__)
|
||||
#define tsrand_fill(amount) _tsrand_fill(amount, __FILE__, __LINE__)
|
||||
|
@ -53,19 +169,4 @@ double _anfrand(int idx, const char *file, uint line);
|
|||
|
||||
#define TSRAND_ARRAY_LIMIT 16
|
||||
|
||||
// Range: [rmin; rmax)
|
||||
INLINE double rand_range(double rmin, double rmax) {
|
||||
// TODO: ensure uniform distribution?
|
||||
return frand() * (rmax - rmin) + rmin;
|
||||
}
|
||||
|
||||
INLINE double rand_angle(void) {
|
||||
// TODO: ensure uniform distribution?
|
||||
return frand() * (M_PI * 2);
|
||||
}
|
||||
|
||||
INLINE bool rand_chance(double chance) {
|
||||
return frand() < chance;
|
||||
}
|
||||
|
||||
#endif // IGUARD_random_h
|
||||
|
|
16
src/stage.c
16
src/stage.c
|
@ -881,13 +881,13 @@ static RenderFrameAction stage_render_frame(void *arg) {
|
|||
return RFRAME_DROP;
|
||||
}
|
||||
|
||||
tsrand_lock(&global.rand_game);
|
||||
tsrand_switch(&global.rand_visual);
|
||||
rng_lock(&global.rand_game);
|
||||
rng_make_active(&global.rand_visual);
|
||||
BEGIN_DRAW_CODE();
|
||||
stage_draw_scene(stage);
|
||||
END_DRAW_CODE();
|
||||
tsrand_unlock(&global.rand_game);
|
||||
tsrand_switch(&global.rand_game);
|
||||
rng_unlock(&global.rand_game);
|
||||
rng_make_active(&global.rand_game);
|
||||
draw_transition();
|
||||
|
||||
return RFRAME_SWAP;
|
||||
|
@ -934,13 +934,13 @@ void stage_enter(StageInfo *stage, CallChain next) {
|
|||
stage_preload();
|
||||
stage_draw_init();
|
||||
|
||||
tsrand_switch(&global.rand_game);
|
||||
rng_make_active(&global.rand_game);
|
||||
stage_start(stage);
|
||||
|
||||
if(global.replaymode == REPLAY_RECORD) {
|
||||
uint64_t start_time = (uint64_t)time(0);
|
||||
uint64_t seed = makeseed();
|
||||
tsrand_seed_p(&global.rand_game, seed);
|
||||
rng_seed(&global.rand_game, seed);
|
||||
|
||||
global.replay_stage = replay_create_stage(&global.replay, stage, start_time, seed, global.diff, &global.plr);
|
||||
|
||||
|
@ -966,7 +966,7 @@ void stage_enter(StageInfo *stage, CallChain next) {
|
|||
assert(stg != NULL);
|
||||
assert(stage_get(stg->stage) == stage);
|
||||
|
||||
tsrand_seed_p(&global.rand_game, stg->rng_seed);
|
||||
rng_seed(&global.rand_game, stg->rng_seed);
|
||||
|
||||
log_debug("REPLAY_PLAY mode: %d events, stage: \"%s\"", stg->numevents, stage->title);
|
||||
log_debug("Start time: %"PRIu64, stg->start_time);
|
||||
|
@ -1017,7 +1017,7 @@ void stage_end_loop(void* ctx) {
|
|||
stage_free();
|
||||
player_free(&global.plr);
|
||||
cosched_finish(&s->sched);
|
||||
tsrand_switch(&global.rand_visual);
|
||||
rng_make_active(&global.rand_visual);
|
||||
free_all_refs();
|
||||
ent_shutdown();
|
||||
stage_objpools_free();
|
||||
|
|
|
@ -107,12 +107,12 @@ static void cirno_iceplosion0(Boss *c, int time) {
|
|||
.pos = c->pos,
|
||||
.color = RGB(0.3,0.3,0.8),
|
||||
.rule = accelerated,
|
||||
.args = { global.diff/4.*cexp(2.0*I*M_PI*frand()) + 2.0*I, 0.002*cexp(I*(M_PI/10.0*(_i%20))) }
|
||||
.args = { global.diff/4.0*rng_dir() + 2.0*I, 0.002*cdir(M_PI/10.0*(_i%20)) }
|
||||
);
|
||||
}
|
||||
|
||||
FROM_TO(150, 300, 30-5*global.diff) {
|
||||
float dif = M_PI*2*frand();
|
||||
float dif = rng_angle();
|
||||
int i;
|
||||
play_sound("shot1");
|
||||
for(i = 0; i < 20; i++) {
|
||||
|
@ -142,14 +142,14 @@ void cirno_crystal_rain(Boss *c, int time) {
|
|||
|
||||
int hdiff = max(0, (int)global.diff - D_Normal);
|
||||
|
||||
if(frand() > 0.95-0.1*global.diff) {
|
||||
tsrand_fill(2);
|
||||
if(rng_chance(0.05 + 0.1 * global.diff)) {
|
||||
RNG_ARRAY(rng, 2);
|
||||
PROJECTILE(
|
||||
.proto = pp_crystal,
|
||||
.pos = VIEWPORT_W*afrand(0),
|
||||
.pos = vrng_range(rng[0], 0, VIEWPORT_W),
|
||||
.color = RGB(0.2,0.2,0.4),
|
||||
.rule = accelerated,
|
||||
.args = { 1.0*I, 0.01*I + (-0.005+0.005*global.diff)*anfrand(1) }
|
||||
.args = { 1.0*I, 0.01*I + (-0.005+0.005*global.diff) * vrng_sreal(rng[1]) }
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -205,7 +205,7 @@ static void cirno_iceplosion1(Boss *c, int time) {
|
|||
.pos = c->pos,
|
||||
.color = RGB(0,0,0.5),
|
||||
.rule = asymptotic,
|
||||
.args = { (3+_i/3.0)*cexp(I*((2)*M_PI/8.0*i + (0.1+0.03*global.diff)*(1 - 2*frand()))), _i*0.7 }
|
||||
.args = { (3+_i/3.0)*cexp(I*((2)*M_PI/8.0*i + (0.1+0.03*global.diff) * rng_sreal())), _i*0.7 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -218,7 +218,7 @@ static void cirno_iceplosion1(Boss *c, int time) {
|
|||
.color = RGB(0.3,0.3,0.8),
|
||||
.rule = accelerated,
|
||||
.args = {
|
||||
1.5*cexp(2.0*I*M_PI*frand()) - i * 0.4 + 2.0*I*global.diff/4.0,
|
||||
1.5*rng_angle() - i * 0.4 + 2.0*I*global.diff/4.0,
|
||||
0.002*cexp(I*(M_PI/10.0*(_i%20)))
|
||||
}
|
||||
);
|
||||
|
@ -226,7 +226,7 @@ static void cirno_iceplosion1(Boss *c, int time) {
|
|||
}
|
||||
|
||||
FROM_TO(150, 300, 30 - 6 * global.diff) {
|
||||
float dif = M_PI*2*frand();
|
||||
float dif = rng_angle();
|
||||
int i;
|
||||
|
||||
if(_i > 15) {
|
||||
|
@ -240,7 +240,7 @@ static void cirno_iceplosion1(Boss *c, int time) {
|
|||
.pos = c->pos,
|
||||
.color = RGB(0.04*_i,0.04*_i,0.4+0.04*_i),
|
||||
.rule = asymptotic,
|
||||
.args = { (3+_i/3.0)*cexp(I*(2*M_PI/8.0*i + dif)), 2.5 }
|
||||
.args = { (3+_i/3.0)*cdir(2*M_PI/8.0*i + dif), 2.5 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -339,7 +339,7 @@ static int halation_orb(Projectile *p, int time) {
|
|||
};
|
||||
|
||||
int pcount = sizeof(colors)/sizeof(Color);
|
||||
float rot = frand() * 2 * M_PI;
|
||||
float rot = rng_angle();
|
||||
|
||||
for(int i = 0; i < pcount; ++i) {
|
||||
PROJECTILE(
|
||||
|
@ -347,7 +347,7 @@ static int halation_orb(Projectile *p, int time) {
|
|||
.pos = p->pos,
|
||||
.color = colors+i,
|
||||
.rule = asymptotic,
|
||||
.args = { cexp(I*(rot + M_PI * 2 * (float)(i+1)/pcount)), 3 }
|
||||
.args = { cdir(rot + M_PI * 2 * (float)(i+1)/pcount), 3 }
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -452,7 +452,7 @@ static int cirno_icicles(Projectile *p, int t) {
|
|||
} else if(t == turn) {
|
||||
p->args[0] = 2.5*cexp(I*(carg(p->args[0])-M_PI/2.0+M_PI*(creal(p->args[0]) > 0)));
|
||||
if(global.diff > D_Normal)
|
||||
p->args[0] += 0.05*nfrand();
|
||||
p->args[0] += rng_range(0, 0.05);
|
||||
play_sound("redirect");
|
||||
spawn_projectile_highlight_effect(p);
|
||||
} else if(t > turn) {
|
||||
|
@ -488,7 +488,7 @@ void cirno_icicle_fall(Boss *c, int time) {
|
|||
|
||||
if(global.diff > D_Easy) {
|
||||
FROM_TO_SND("shot1_loop",120,200,3) {
|
||||
float f = frand()*_i;
|
||||
float f = rng_range(0, _i);
|
||||
|
||||
PROJECTILE(.proto = pp_ball, .pos = c->pos, .color = RGB(0.,0.,0.3), .rule = accelerated, .args = { 0.2*(-2*I-1.5+f),-0.02*I });
|
||||
PROJECTILE(.proto = pp_ball, .pos = c->pos, .color = RGB(0.,0.,0.3), .rule = accelerated, .args = { 0.2*(-2*I+1.5+f),-0.02*I });
|
||||
|
@ -499,8 +499,8 @@ void cirno_icicle_fall(Boss *c, int time) {
|
|||
FROM_TO(300,400,10) {
|
||||
play_sound("shot1");
|
||||
float x = VIEWPORT_W/2+VIEWPORT_W/2*(0.3+_i/10.);
|
||||
float angle1 = M_PI/10*frand();
|
||||
float angle2 = M_PI/10*frand();
|
||||
float angle1 = rng_range(0, M_PI/10);
|
||||
float angle2 = rng_range(0, M_PI/10);
|
||||
for(float i = 1; i < 5; i++) {
|
||||
PROJECTILE(
|
||||
.proto = pp_ball,
|
||||
|
@ -508,8 +508,8 @@ void cirno_icicle_fall(Boss *c, int time) {
|
|||
.color = RGB(0.,0.,0.3),
|
||||
.rule = accelerated,
|
||||
.args = {
|
||||
i*I*0.5*cexp(I*angle1),
|
||||
0.001*I-(global.diff == D_Lunatic)*0.001*frand()
|
||||
i*I*0.5*cdir(angle1),
|
||||
0.001*I-(global.diff == D_Lunatic)*0.001*rng_real()
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -519,8 +519,8 @@ void cirno_icicle_fall(Boss *c, int time) {
|
|||
.color = RGB(0.,0.,0.3),
|
||||
.rule = accelerated,
|
||||
.args = {
|
||||
i*I*0.5*cexp(-I*angle2),
|
||||
0.001*I+(global.diff == D_Lunatic)*0.001*frand()
|
||||
i*I*0.5*cdir(-angle2),
|
||||
0.001*I+(global.diff == D_Lunatic)*0.001*rng_real()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -579,14 +579,14 @@ void cirno_crystal_blizzard(Boss *c, int time) {
|
|||
GO_TO(c, global.plr.pos, 0.01);
|
||||
|
||||
if(!(time % (1 + D_Lunatic - global.diff))) {
|
||||
tsrand_fill(2);
|
||||
RNG_ARRAY(rng, 2);
|
||||
PROJECTILE(
|
||||
.proto = pp_wave,
|
||||
.pos = c->pos,
|
||||
.color = RGBA(0.2, 0.2, 0.4, 0.0),
|
||||
.rule = cirno_crystal_blizzard_proj,
|
||||
.args = {
|
||||
20 * (0.1 + 0.1 * anfrand(0)) * cexp(I*(carg(global.plr.pos - c->pos) + anfrand(1) * 0.2)),
|
||||
20 * (0.1 + 0.1 * vrng_sreal(rng[0])) * cexp(I*(carg(global.plr.pos - c->pos) + vrng_sreal(rng[1]) * 0.2)),
|
||||
5
|
||||
},
|
||||
);
|
||||
|
@ -621,7 +621,7 @@ void cirno_benchmark(Boss* b, int t) {
|
|||
double speed = 10;
|
||||
int c = N*speed/VIEWPORT_H;
|
||||
for(int i = 0; i < c; i++) {
|
||||
double x = frand()*VIEWPORT_W;
|
||||
double x = rng_range(0, VIEWPORT_W);
|
||||
double plrx = creal(global.plr.pos);
|
||||
x = plrx + sqrt((x-plrx)*(x-plrx)+100)*(1-2*(x<plrx));
|
||||
|
||||
|
@ -634,17 +634,17 @@ void cirno_benchmark(Boss* b, int t) {
|
|||
.flags = PFLAG_NOGRAZE,
|
||||
);
|
||||
|
||||
if(frand() < 0.1) {
|
||||
if(rng_chance(0.1)) {
|
||||
p->flags &= ~PFLAG_NOGRAZE;
|
||||
}
|
||||
|
||||
if(t > 700 && frand() > 0.5)
|
||||
if(t > 700 && rng_chance(0.5))
|
||||
projectile_set_prototype(p, pp_plainball);
|
||||
|
||||
if(t > 1200 && frand() > 0.5)
|
||||
if(t > 1200 && rng_chance(0.5))
|
||||
p->color = *RGB(1.0, 0.2, 0.8);
|
||||
|
||||
if(t > 350 && frand() > 0.5)
|
||||
if(t > 350 && rng_chance(0.5))
|
||||
p->color.a = 0;
|
||||
}
|
||||
}
|
||||
|
@ -695,14 +695,14 @@ static int stage1_circletoss(Enemy *e, int time) {
|
|||
|
||||
if(global.diff > D_Easy) {
|
||||
FROM_TO_INT_SND("shot1_loop",90,500,150,5+7*global.diff,1) {
|
||||
tsrand_fill(2);
|
||||
RNG_ARRAY(rng, 2);
|
||||
PROJECTILE(
|
||||
.proto = pp_thickrice,
|
||||
.pos = e->pos,
|
||||
.color = RGB(0.2, 0.4, 0.8),
|
||||
.rule = asymptotic,
|
||||
.args = {
|
||||
(1+afrand(0)*2)*cexp(I*carg(global.plr.pos - e->pos)+0.05*I*global.diff*anfrand(1)),
|
||||
(1+vrng_real(rng[0])*2)*cexp(I*carg(global.plr.pos - e->pos)+0.05*I*global.diff*vrng_sreal(rng[1])),
|
||||
3
|
||||
}
|
||||
);
|
||||
|
@ -799,10 +799,10 @@ TASK(circletoss_shoot_toss, { BoxedEnemy e; int times; int duration; int period;
|
|||
play_loop("shot1_loop");
|
||||
|
||||
double aim_angle = carg(global.plr.pos - e->pos);
|
||||
aim_angle += 0.05 * global.diff * nfrand();
|
||||
aim_angle += 0.05 * global.diff * rng_real();
|
||||
|
||||
complex aim = cdir(aim_angle);
|
||||
aim *= rand_range(1, 3);
|
||||
aim *= rng_range(1, 3);
|
||||
|
||||
PROJECTILE(
|
||||
.proto = pp_thickrice,
|
||||
|
@ -907,7 +907,7 @@ TASK(circle_fairy, { complex pos; complex target_pos; }) {
|
|||
int round_interval = 120 - shot_interval * shot_count;
|
||||
|
||||
for(int round = 0; round < 2; ++round) {
|
||||
double a_ofs = rand_angle();
|
||||
double a_ofs = rng_angle();
|
||||
|
||||
for(int i = 0; i < shot_count; ++i) {
|
||||
complex aim;
|
||||
|
@ -926,14 +926,14 @@ TASK(circle_fairy, { complex pos; complex target_pos; }) {
|
|||
WAIT(shot_interval);
|
||||
}
|
||||
|
||||
e->move.attraction_point += 30 * cdir(rand_angle());
|
||||
e->move.attraction_point += 30 * rng_dir();
|
||||
WAIT(round_interval);
|
||||
}
|
||||
|
||||
WAIT(10);
|
||||
e->move.attraction = 0;
|
||||
e->move.retention = 1;
|
||||
e->move.acceleration = -0.04 * I * cdir(nfrand() * M_TAU / 12);
|
||||
e->move.acceleration = -0.04 * I * cdir(rng_range(0, M_TAU / 12));
|
||||
STALL;
|
||||
}
|
||||
|
||||
|
@ -952,7 +952,7 @@ TASK(drop_swirl, { complex pos; complex vel; complex accel; }) {
|
|||
|
||||
while(true) {
|
||||
complex aim = cnormalize(global.plr.pos - e->pos);
|
||||
aim *= 1 + 0.3 * global.diff + frand();
|
||||
aim *= 1 + 0.3 * global.diff + rng_real();
|
||||
|
||||
play_sound("shot1");
|
||||
PROJECTILE(
|
||||
|
@ -1083,7 +1083,7 @@ TASK(burst_fairies_2, NO_ARGS) {
|
|||
TASK(burst_fairies_3, NO_ARGS) {
|
||||
for(int i = 10; i--;) {
|
||||
complex pos = VIEWPORT_W/2 - 200 * sin(1.17 * global.frames);
|
||||
INVOKE_TASK(burst_fairy, pos, sign(nfrand()));
|
||||
INVOKE_TASK(burst_fairy, pos, rng_sign());
|
||||
stage_wait(60);
|
||||
}
|
||||
}
|
||||
|
@ -1150,7 +1150,7 @@ TASK(multiburst_fairies_1, NO_ARGS) {
|
|||
for(int row = 0; row < 3; ++row) {
|
||||
for(int col = 0; col < 5; ++col) {
|
||||
log_debug("WTF %i %i", row, col);
|
||||
complex pos = VIEWPORT_W * frand();
|
||||
complex pos = rng_range(0, VIEWPORT_W);
|
||||
complex target_pos = 64 + 64 * col + I * (64 * row + 100);
|
||||
complex exit_accel = 0.02 * I + 0.03;
|
||||
INVOKE_TASK(multiburst_fairy, pos, target_pos, exit_accel);
|
||||
|
@ -1308,18 +1308,18 @@ DEFINE_EXTERN_TASK(stage1_spell_perfect_freeze) {
|
|||
for(int i = 0; i < nfrog/n; i++) {
|
||||
play_loop("shot1_loop");
|
||||
|
||||
float r = frand();
|
||||
float g = frand();
|
||||
float b = frand();
|
||||
float r = rng_f32();
|
||||
float g = rng_f32();
|
||||
float b = rng_f32();
|
||||
|
||||
for(int j = 0; j < n; j++) {
|
||||
float speed = 1+(4+0.5*global.diff)*frand();
|
||||
float speed = rng_range(1.0f, 5.0f + 0.5f * global.diff);
|
||||
|
||||
ENT_ARRAY_ADD(&projs, PROJECTILE(
|
||||
.proto = pp_ball,
|
||||
.pos = boss->pos,
|
||||
.color = RGB(r, g, b),
|
||||
.move = move_linear(speed*cexp(I*tsrand())),
|
||||
.move = move_linear(speed * rng_dir()),
|
||||
));
|
||||
}
|
||||
YIELD;
|
||||
|
@ -1333,14 +1333,14 @@ DEFINE_EXTERN_TASK(stage1_spell_perfect_freeze) {
|
|||
play_sound("shot_special1");
|
||||
|
||||
p->color = *RGB(0.9, 0.9, 0.9);
|
||||
p->move.retention = 0.8*cdir(rand_angle());
|
||||
if(frand() < 0.2) {
|
||||
p->move.retention = 0.8 * rng_dir();
|
||||
if(rng_chance(0.2)) {
|
||||
YIELD;
|
||||
}
|
||||
});
|
||||
|
||||
WAIT(60);
|
||||
int dir = 2*(frand()>0.5)-1; // wait till they figure this out
|
||||
double dir = rng_sign();
|
||||
boss->move = (MoveParams){ .velocity = dir*2.7+I, .retention = 0.99, .acceleration = -dir*0.017 };
|
||||
|
||||
aniplayer_queue(&boss->ani,"(9)",0);
|
||||
|
@ -1349,12 +1349,13 @@ DEFINE_EXTERN_TASK(stage1_spell_perfect_freeze) {
|
|||
|
||||
ENT_ARRAY_FOREACH(&projs, Projectile *p, {
|
||||
p->color = *RGB(0.9, 0.9, 0.9);
|
||||
p->move.retention = 1+0.002*global.diff*frand();
|
||||
p->move.velocity = 2*cdir(rand_angle());
|
||||
p->move.retention = 1 + 0.002 * global.diff * rng_f64();
|
||||
p->move.velocity = 2 * rng_dir();
|
||||
spawn_stain(p->pos, p->angle, 30);
|
||||
spawn_projectile_highlight_effect(p);
|
||||
play_sound_ex("shot2", 0, false);
|
||||
if(frand() < 0.4) {
|
||||
|
||||
if(rng_chance(0.4)) {
|
||||
YIELD;
|
||||
}
|
||||
});
|
||||
|
@ -1367,8 +1368,8 @@ DEFINE_EXTERN_TASK(stage1_spell_perfect_freeze) {
|
|||
r1 = sin(i/M_PI*5.3) * cos(2*i/M_PI*5.3);
|
||||
r2 = cos(i/M_PI*5.3) * sin(2*i/M_PI*5.3);
|
||||
} else {
|
||||
r1 = nfrand();
|
||||
r2 = nfrand();
|
||||
r1 = rng_f32();
|
||||
r2 = rng_f32();
|
||||
}
|
||||
|
||||
complex aim = cnormalize(global.plr.pos - boss->pos);
|
||||
|
@ -1428,7 +1429,7 @@ TASK(tritoss_fairy, { complex pos; complex velocity; complex end_velocity; }) {
|
|||
for(int k = 0; k < rounds; k++) {
|
||||
play_sound("shot1");
|
||||
|
||||
float a = M_PI / 30.0 * ((k/7) % 30) + 0.1 * nfrand();
|
||||
float a = M_PI / 30.0 * ((k/7) % 30) + 0.1 * rng_f32();
|
||||
int n = difficulty_value(3,4,4,5);
|
||||
|
||||
for(int i = 0; i < n; i++){
|
||||
|
@ -1515,7 +1516,11 @@ TASK(stage_timeline, NO_ARGS) {
|
|||
STAGE_BOOKMARK(post-midboss-filler);
|
||||
|
||||
for(int i = 0; i < 5; i++) {
|
||||
INVOKE_TASK_DELAYED(i*200, multiburst_fairy, VIEWPORT_W/2 - 195 * cos(2.43 * i) - 20 * I, VIEWPORT_W / 2 - 100 * cos(2.43 * i) + 100 * I, 0.1 * sign(nfrand()));
|
||||
INVOKE_TASK_DELAYED(i*200, multiburst_fairy,
|
||||
VIEWPORT_W/2 - 195 * cos(2.43 * i) - 20 * I,
|
||||
VIEWPORT_W / 2 - 100 * cos(2.43 * i) + 100 * I,
|
||||
0.1 * rng_sign()
|
||||
);
|
||||
}
|
||||
|
||||
for(int i = 0; i < difficulty_value(2, 2, 4, 4); i++) {
|
||||
|
|
|
@ -309,4 +309,8 @@ typedef cmplx64 cmplx;
|
|||
#define abort nxAbort
|
||||
#endif
|
||||
|
||||
#ifdef RNG_API_CHECK
|
||||
#define _Generic(ignore, ...) _Generic(0, __VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#endif // IGUARD_util_compat_h
|
||||
|
|
Loading…
Reference in a new issue