/* SPDX-License-Identifier: MIT */ /* * konrmfield.c * * Copyright (c) 2026 KonAki * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "konrmfield.h" #include #include /*** helpers ***/ static int validDir(int dir) { return dir == 2 || dir == 4 || dir == 6 || dir == 8; } static int clampInt(int v, int lo, int hi) { return v < lo ? lo : v > hi ? hi : v; } static int roll(kon_rmField_t *field, int n) { uint32_t s = field->seed; s ^= s << 13; s ^= s >> 17; s ^= s << 5; field->seed = s ? s : 1; return n > 0 ? (int)(field->seed % (uint32_t)n) : 0; } static int dirX(int dir) { return dir == 6 ? 1 : dir == 4 ? -1 : 0; } static int dirY(int dir) { return dir == 2 ? 1 : dir == 8 ? -1 : 0; } /* how far a is right of b, the short way round on a map that loops */ static int deltaX(const kon_rmMap_t *map, int a, int b) { int r = a - b; if ((map->scrollType == 2 || map->scrollType == 3) && (r < 0 ? -r : r) > map->width / 2) r += r < 0 ? map->width : -map->width; return r; } static int deltaY(const kon_rmMap_t *map, int a, int b) { int r = a - b; if ((map->scrollType == 1 || map->scrollType == 3) && (r < 0 ? -r : r) > map->height / 2) r += r < 0 ? map->height : -map->height; return r; } static int moving(const kon_rmChar_t *c) { return c->remain > 0 || c->jumpCount > 0; } static void setDir(kon_rmChar_t *c, int dir) { if (!c->dirFix && validDir(dir)) c->dir = dir; c->stopCount = 0; } /*** what blocks what ***/ static int collidedWithEvents(const kon_rmField_t *field, int self, int x, int y, int normalOnly) { int i; for (i = 1; i < field->count; i++) { const kon_rmChar_t *e = &field->chars[i]; if (i == self || !e->active || e->through || e->x != x || e->y != y) continue; if (!normalOnly || e->priority == 1) return 1; } return 0; } static int canPass(const kon_rmField_t *field, int index, int x, int y, int dir) { const kon_rmChar_t *c = &field->chars[index]; const kon_rmChar_t *player = &field->chars[KON_RM_PLAYER]; int x2 = kon_rmRoundX(field->map, x + dirX(dir)); int y2 = kon_rmRoundY(field->map, y + dirY(dir)); if (!kon_rmValid(field->map, x2, y2)) return 0; if (c->through) return 1; if (!kon_rmPassable(field->map, field->tileset, x, y, dir)) return 0; if (!kon_rmPassable(field->map, field->tileset, x2, y2, 10 - dir)) return 0; if (index == KON_RM_PLAYER) return !collidedWithEvents(field, -1, x2, y2, 1); if (collidedWithEvents(field, index, x2, y2, 0)) return 0; return !(c->priority == 1 && player->active && !player->through && player->x == x2 && player->y == y2); } static int canPassDiagonally(const kon_rmField_t *field, int index, int x, int y, int horz, int vert) { int x2 = kon_rmRoundX(field->map, x + dirX(horz)); int y2 = kon_rmRoundY(field->map, y + dirY(vert)); if (canPass(field, index, x, y, vert) && canPass(field, index, x, y2, horz)) return 1; return canPass(field, index, x, y, horz) && canPass(field, index, x2, y, vert); } /*** starting events ***/ static void start(kon_rmField_t *field, int index) { kon_rmChar_t *e = &field->chars[index]; if (!e->active || !e->page) return; if (field->hasScript && !field->hasScript(e->page->script, field->user)) return; e->starting = 1; if (e->trigger <= 2 && !e->locked) { const kon_rmChar_t *p = &field->chars[KON_RM_PLAYER]; int sx = deltaX(field->map, e->x, p->x), sy = deltaY(field->map, e->y, p->y); e->preLockDir = e->dir; if ((sx < 0 ? -sx : sx) > (sy < 0 ? -sy : sy)) setDir(e, sx > 0 ? 4 : 6); else if (sy != 0) setDir(e, sy > 0 ? 8 : 2); e->locked = 1; } } /* events on x, y that start with one of the triggers (bits of mask), the ones at the same height as the characters or the others */ static int startAt(kon_rmField_t *field, int x, int y, int mask, int normal) { int i, n = 0; if (field->busy || !kon_rmValid(field->map, x, y)) return 0; for (i = 1; i < field->count; i++) { kon_rmChar_t *e = &field->chars[i]; if (!e->active || e->x != x || e->y != y || !((mask >> e->trigger) & 1) || (e->priority == 1) != normal) continue; start(field, i); if (e->starting) n++; } return n; } static void touchFront(kon_rmField_t *field, int index, int dir) { const kon_rmChar_t *c = &field->chars[index]; int x2 = kon_rmRoundX(field->map, c->x + dirX(dir)); int y2 = kon_rmRoundY(field->map, c->y + dirY(dir)); if (field->busy) return; if (index == KON_RM_PLAYER) { startAt(field, x2, y2, 0x06, 1); } else if (c->trigger == 2 && c->priority == 1 && c->jumpCount == 0) { const kon_rmChar_t *p = &field->chars[KON_RM_PLAYER]; if (p->active && p->x == x2 && p->y == y2) start(field, index); } } /*** steps ***/ static int step(kon_rmField_t *field, int index, int dir) { kon_rmChar_t *c = &field->chars[index]; int ok = canPass(field, index, c->x, c->y, dir); c->success = ok; setDir(c, dir); if (ok) { c->moveDx = dirX(dir); c->moveDy = dirY(dir); c->x = kon_rmRoundX(field->map, c->x + c->moveDx); c->y = kon_rmRoundY(field->map, c->y + c->moveDy); c->remain = 256; c->stopCount = 0; c->steps++; if (index == KON_RM_PLAYER) field->steps++; } else { touchFront(field, index, dir); } return ok; } static void stepDiagonal(kon_rmField_t *field, int index, int horz, int vert) { kon_rmChar_t *c = &field->chars[index]; int ok = canPassDiagonally(field, index, c->x, c->y, horz, vert); c->success = ok; if (ok) { c->moveDx = dirX(horz); c->moveDy = dirY(vert); c->x = kon_rmRoundX(field->map, c->x + c->moveDx); c->y = kon_rmRoundY(field->map, c->y + c->moveDy); c->remain = 256; c->stopCount = 0; c->steps++; if (index == KON_RM_PLAYER) field->steps++; } if (c->dir == 10 - horz) setDir(c, horz); if (c->dir == 10 - vert) setDir(c, vert); } static void stepRandom(kon_rmField_t *field, int index) { kon_rmChar_t *c = &field->chars[index]; int dir = 2 + roll(field, 4) * 2; if (canPass(field, index, c->x, c->y, dir)) step(field, index, dir); } static void stepToward(kon_rmField_t *field, int index, int away) { kon_rmChar_t *c = &field->chars[index]; const kon_rmChar_t *p = &field->chars[KON_RM_PLAYER]; int sx = deltaX(field->map, c->x, p->x), sy = deltaY(field->map, c->y, p->y); int h = (sx > 0) != away ? 4 : 6, v = (sy > 0) != away ? 8 : 2; if ((sx < 0 ? -sx : sx) > (sy < 0 ? -sy : sy)) { step(field, index, h); if (!c->success && sy != 0) step(field, index, v); } else if (sy != 0) { step(field, index, v); if (!c->success && sx != 0) step(field, index, h); } } static void turnToward(kon_rmField_t *field, int index, int away) { kon_rmChar_t *c = &field->chars[index]; const kon_rmChar_t *p = &field->chars[KON_RM_PLAYER]; int sx = deltaX(field->map, c->x, p->x), sy = deltaY(field->map, c->y, p->y); int h = (sx > 0) != away ? 4 : 6, v = (sy > 0) != away ? 8 : 2; if ((sx < 0 ? -sx : sx) > (sy < 0 ? -sy : sy)) setDir(c, h); else if (sy != 0) setDir(c, v); } static void jump(kon_rmField_t *field, int index, int dx, int dy) { kon_rmChar_t *c = &field->chars[index]; int x2 = kon_rmRoundX(field->map, c->x + dx), y2 = kon_rmRoundY(field->map, c->y + dy); int d2 = dx * dx + dy * dy, dist = 0; if (dx > 1000 || dx < -1000 || dy > 1000 || dy < -1000 || !kon_rmValid(field->map, x2, y2)) return; if (dx != 0 || dy != 0) { if ((dx < 0 ? -dx : dx) > (dy < 0 ? -dy : dy)) setDir(c, dx < 0 ? 4 : 6); else if (dy != 0) setDir(c, dy < 0 ? 8 : 2); } while ((dist + 1) * (dist + 1) <= d2 + d2 / 4 + 1) dist++; /* about the length, rounded */ c->x = x2; c->y = y2; c->jumpDx = dx; c->jumpDy = dy; c->jumpPeak = clampInt(10 + dist - c->moveSpeed, 1, 2000); c->jumpCount = c->jumpPeak * 2; c->stopCount = 0; c->steps++; } /*** routes ***/ static void runCommand(kon_rmField_t *field, int index, int code, int count, const int32_t *p) { kon_rmChar_t *c = &field->chars[index]; int a = count > 0 ? p[0] : 0; switch (code) { case 1: case 2: case 3: case 4: step(field, index, code == 1 ? 2 : code == 2 ? 4 : code == 3 ? 6 : 8); break; case 5: stepDiagonal(field, index, 4, 2); break; case 6: stepDiagonal(field, index, 6, 2); break; case 7: stepDiagonal(field, index, 4, 8); break; case 8: stepDiagonal(field, index, 6, 8); break; case 9: stepRandom(field, index); break; case 10: stepToward(field, index, 0); break; case 11: stepToward(field, index, 1); break; case 12: step(field, index, c->dir); break; case 13: { int fix = c->dirFix; c->dirFix = 1; step(field, index, 10 - c->dir); c->dirFix = fix; break; } case 14: jump(field, index, a, count > 1 ? p[1] : 0); break; case 15: c->routeWait = a > 1 ? (a > 1000000 ? 1000000 : a) - 1 : 0; break; case 16: setDir(c, 2); break; case 17: setDir(c, 4); break; case 18: setDir(c, 6); break; case 19: setDir(c, 8); break; case 20: setDir(c, c->dir == 2 ? 4 : c->dir == 4 ? 8 : c->dir == 8 ? 6 : 2); break; case 21: setDir(c, c->dir == 2 ? 6 : c->dir == 6 ? 8 : c->dir == 8 ? 4 : 2); break; case 22: setDir(c, 10 - c->dir); break; case 23: setDir(c, roll(field, 2) ? (c->dir == 2 ? 6 : c->dir == 6 ? 8 : c->dir == 8 ? 4 : 2) : (c->dir == 2 ? 4 : c->dir == 4 ? 8 : c->dir == 8 ? 6 : 2)); break; case 24: setDir(c, 2 + roll(field, 4) * 2); break; case 25: turnToward(field, index, 0); break; case 26: turnToward(field, index, 1); break; case 27: case 28: if (field->switchSet && count > 0) field->switchSet(a, code == 27, field->user); break; case 29: c->moveSpeed = clampInt(a, 1, 6); break; case 30: c->moveFrequency = clampInt(a, 1, 5); break; case 31: c->walkAnime = 1; break; case 32: c->walkAnime = 0; break; case 33: c->stepAnime = 1; break; case 34: c->stepAnime = 0; break; case 35: c->dirFix = 1; break; case 36: c->dirFix = 0; break; case 37: c->through = 1; break; case 38: c->through = 0; break; case 39: c->transparent = 1; break; case 40: c->transparent = 0; break; case 42: c->opacity = clampInt(a, 0, 255); break; default: break; /* the image, blend, sound and script commands are not here */ } } static void routeStep(kon_rmField_t *field, int index) { kon_rmChar_t *c = &field->chars[index]; int code, count; if (c->routeWait > 0) { c->routeWait--; return; } c->success = 1; if (!c->route || c->routeIndex < 0 || c->routeIndex + 2 > c->routeSize) { /* the end of the route */ if (c->routeRepeat) c->routeIndex = 0; else if (c->routeForced) c->routeForced = 0; return; } code = c->route[c->routeIndex]; count = c->route[c->routeIndex + 1]; if (count < 0 || count > 8 || c->routeIndex + 2 + count > c->routeSize) { c->routeIndex = c->routeSize; return; } runCommand(field, index, code, count, c->route + c->routeIndex + 2); if (c->success || c->routeSkippable) c->routeIndex += 2 + count; } /*** pages ***/ static void setupPage(kon_rmField_t *field, kon_rmChar_t *c, int pageIndex, const kon_rmPage_t *page) { int dir = validDir(page->direction) ? page->direction : 2; c->pageIndex = pageIndex; c->page = page; c->active = 1; if (c->originalDir != dir) { c->originalDir = dir; c->preLockDir = 0; c->dirFix = 0; c->dir = dir; } c->moveSpeed = clampInt(page->moveSpeed, 1, 6); c->moveFrequency = clampInt(page->moveFrequency, 1, 5); c->moveType = clampInt(page->moveType, 0, 3); c->priority = clampInt(page->priority, 0, 2); c->trigger = clampInt(page->trigger, 0, 4); c->walkAnime = page->walkAnime; c->stepAnime = page->stepAnime; c->dirFix = page->directionFix; c->through = page->through; c->transparent = 0; c->opacity = 255; c->route = page->route; c->routeSize = page->route ? page->routeSize : 0; c->routeIndex = 0; c->routeWait = 0; c->routeRepeat = page->routeRepeat; c->routeSkippable = page->routeSkippable; c->routeForced = 0; c->locked = 0; c->starting = 0; c->stopCount = 0; (void)field; } static void clearPage(kon_rmChar_t *c) { c->pageIndex = -1; c->page = NULL; c->active = 0; c->locked = 0; c->starting = 0; c->route = NULL; c->routeSize = 0; c->routeForced = 0; } void kon_rmFieldRefresh(kon_rmField_t *field) { int i; if (!field || !field->map) return; for (i = 0; i < field->map->eventCount && i + 1 < field->count; i++) { const kon_rmEvent_t *event = &field->map->events[i]; kon_rmChar_t *c = &field->chars[i + 1]; int page = c->erased ? -1 : kon_rmEventPage(event, field->map->id, field->state); if (page == c->pageIndex) continue; if (page < 0) clearPage(c); else setupPage(field, c, page, &event->pages[page]); } } /*** the field ***/ int kon_rmFieldInit(kon_rmField_t *field, const kon_rmMap_t *map, const kon_rmTileset_t *tileset, const kon_rmState_t *state, kon_rmChar_t *chars, int capacity) { int i; if (!field || !map || !chars || capacity < map->eventCount + 1 || map->eventCount < 0) return -1; memset(field, 0, sizeof *field); field->map = map; field->tileset = tileset; field->state = state; field->chars = chars; field->count = map->eventCount + 1; field->seed = 2463534242u; field->waitIndex = -1; memset(chars, 0, sizeof *chars * (size_t)field->count); for (i = 0; i < field->count; i++) { kon_rmChar_t *c = &chars[i]; c->dir = 2; c->originalDir = 2; c->moveSpeed = 4; c->moveFrequency = 3; c->priority = 1; c->opacity = 255; c->pageIndex = -1; if (i > 0) { c->x = map->events[i - 1].x; c->y = map->events[i - 1].y; c->eventId = map->events[i - 1].id; } } chars[KON_RM_PLAYER].active = 1; chars[KON_RM_PLAYER].trigger = 0; kon_rmFieldRefresh(field); return 0; } void kon_rmFieldSeed(kon_rmField_t *field, uint32_t seed) { if (field) field->seed = seed ? seed : 1; } void kon_rmFieldPlace(kon_rmField_t *field, int x, int y, int dir) { kon_rmChar_t *p; if (!field || !field->chars) return; p = &field->chars[KON_RM_PLAYER]; if (kon_rmValid(field->map, x, y)) { p->x = kon_rmRoundX(field->map, x); p->y = kon_rmRoundY(field->map, y); } p->remain = 0; p->jumpCount = 0; if (validDir(dir)) p->dir = dir; } static int stopThreshold(const kon_rmChar_t *c) { return 30 * (5 - c->moveFrequency); } static void selfMove(kon_rmField_t *field, int index) { kon_rmChar_t *c = &field->chars[index]; const kon_rmChar_t *p = &field->chars[KON_RM_PLAYER]; if (c->stopCount <= stopThreshold(c)) return; switch (c->moveType) { case 1: switch (roll(field, 6)) { case 0: case 1: stepRandom(field, index); break; case 2: case 3: case 4: step(field, index, c->dir); break; default: c->stopCount = 0; break; } break; case 2: { int sx = deltaX(field->map, c->x, p->x), sy = deltaY(field->map, c->y, p->y); if ((sx < 0 ? -sx : sx) + (sy < 0 ? -sy : sy) < 20) { switch (roll(field, 6)) { case 0: case 1: case 2: case 3: stepToward(field, index, 0); break; case 4: stepRandom(field, index); break; default: step(field, index, c->dir); break; } } else { stepRandom(field, index); } break; } case 3: routeStep(field, index); break; default: break; } } static void updateChar(kon_rmField_t *field, int index) { kon_rmChar_t *c = &field->chars[index]; if (c->jumpCount > 0) { c->jumpCount--; return; } if (c->remain > 0) { c->remain -= 1 << clampInt(c->moveSpeed + c->dashing, 1, 8); if (c->remain <= 0) { c->remain = 0; c->dashing = 0; } return; } c->dashing = 0; c->stopCount++; if (c->routeForced) routeStep(field, index); else if (index > 0 && !c->locked && c->moveType) selfMove(field, index); } void kon_rmFieldUpdate(kon_rmField_t *field) { kon_rmChar_t *p; int i, wasMoving; if (!field || !field->chars) return; p = &field->chars[KON_RM_PLAYER]; wasMoving = moving(p); for (i = 0; i < field->count; i++) { if (field->chars[i].active) updateChar(field, i); } for (i = 1; i < field->count; i++) { kon_rmChar_t *e = &field->chars[i]; if (e->active && e->trigger == 3 && !field->busy) start(field, i); } if (wasMoving && !moving(p) && !field->busy) startAt(field, p->x, p->y, 0x06, 0); } /*** the player ***/ int kon_rmFieldPlayerStep(kon_rmField_t *field, int dir, int dash) { kon_rmChar_t *p; if (!field || !field->chars || !validDir(dir)) return 0; p = &field->chars[KON_RM_PLAYER]; if (field->busy || moving(p) || p->routeForced) return 0; step(field, KON_RM_PLAYER, dir); if (p->success && dash && !field->map->disableDashing) p->dashing = 1; return p->success; } int kon_rmFieldAction(kon_rmField_t *field) { const kon_rmChar_t *p; int x, y, x2, y2, n; if (!field || !field->chars) return 0; p = &field->chars[KON_RM_PLAYER]; if (field->busy || moving(p)) return 0; if (startAt(field, p->x, p->y, 0x01, 0)) return 1; x = kon_rmRoundX(field->map, p->x + dirX(p->dir)); y = kon_rmRoundY(field->map, p->y + dirY(p->dir)); n = startAt(field, x, y, 0x07, 1); if (n == 0 && kon_rmHasFlag(field->map, field->tileset, x, y, KON_RM_FLAG_COUNTER)) { x2 = kon_rmRoundX(field->map, x + dirX(p->dir)); y2 = kon_rmRoundY(field->map, y + dirY(p->dir)); n = startAt(field, x2, y2, 0x07, 1); } return n > 0; } /*** events that start ***/ int kon_rmFieldNextStart(kon_rmField_t *field, int *eventId, int *pageIndex) { int i; if (!field || !field->chars) return 0; for (i = 1; i < field->count; i++) { kon_rmChar_t *e = &field->chars[i]; if (!e->active || !e->starting) continue; e->starting = 0; if (eventId) *eventId = e->eventId; if (pageIndex) *pageIndex = e->pageIndex; return 1; } return 0; } int kon_rmFieldFind(const kon_rmField_t *field, int eventId) { int i; if (!field || !field->chars) return -1; for (i = 1; i < field->count; i++) { if (field->chars[i].eventId == eventId) return i; } return -1; } void kon_rmFieldUnlock(kon_rmField_t *field, int index) { kon_rmChar_t *c; if (!field || !field->chars || index < 0 || index >= field->count) return; c = &field->chars[index]; if (!c->locked) return; c->locked = 0; if (validDir(c->preLockDir)) setDir(c, c->preLockDir); } /*** moving by command ***/ void kon_rmFieldRoute(kon_rmField_t *field, int index, const int32_t *route, int size, int repeat, int skippable) { kon_rmChar_t *c; if (!field || !field->chars || index < 0 || index >= field->count) return; c = &field->chars[index]; c->route = route; c->routeSize = route && size > 0 ? size : 0; c->routeIndex = 0; c->routeWait = 0; c->routeRepeat = repeat; c->routeSkippable = skippable; c->routeForced = 1; } int kon_rmFieldRouteBusy(const kon_rmField_t *field, int index) { if (!field || !field->chars || index < 0 || index >= field->count) return 0; return field->chars[index].routeForced; } /*** commands from scripts ***/ /* the numbers of a text the way mzscript writes them, apart by tabs. what is not a number is 0 */ static int parseInts(const char *text, int *out, int max) { int n = 0; if (!text) return 0; while (*text && n < max) { long v = 0; int neg = 0, any = 0; if (*text == '-') { neg = 1; text++; } while (*text >= '0' && *text <= '9') { if (v < 100000000) v = v * 10 + (*text - '0'); any = 1; text++; } out[n++] = any ? (int)(neg ? -v : v) : 0; while (*text && *text != '\t') text++; if (*text == '\t') text++; else break; } return n; } static int variable(const kon_rmField_t *field, int id) { return field->state && field->state->variable ? field->state->variable(id, field->state->user) : 0; } /* -1 is the player, 0 the event the script belongs to, the others are events by id */ static int charIndex(const kon_rmField_t *field, int id) { int i; if (id == -1) return KON_RM_PLAYER; if (id == 0) id = field->thisEvent; if (id <= 0) return -1; i = kon_rmFieldFind(field, id); return i >= 0 && field->chars[i].active ? i : -1; } static void locate(kon_rmField_t *field, int index, int x, int y, int dir) { kon_rmChar_t *c = &field->chars[index]; if (!kon_rmValid(field->map, x, y)) return; c->x = kon_rmRoundX(field->map, x); c->y = kon_rmRoundY(field->map, y); c->remain = 0; c->jumpCount = 0; c->stopCount = 0; if (validDir(dir)) setDir(c, dir); } static void hostRoute(kon_rmField_t *field, int index, int flags, const char *text) { kon_rmChar_t *c = &field->chars[index]; int size = 0; const char *s = text; /* the route is read command by command, the whole ones that fit */ while (s && *s) { int head[2], numbers[8], n, i; const char *next = s; if (parseInts(s, head, 2) < 2 || head[1] < 0 || head[1] > 8 || size + 2 + head[1] > KON_RM_ROUTE_MAX) break; for (i = 0; i < 2 && *next; i++) { while (*next && *next != '\t') next++; if (*next == '\t') next++; } n = parseInts(next, numbers, head[1]); if (n < head[1]) break; c->routeData[size++] = head[0]; c->routeData[size++] = head[1]; for (i = 0; i < head[1]; i++) c->routeData[size++] = numbers[i]; for (i = 0; i < head[1]; i++) { while (*next && *next != '\t') next++; if (*next == '\t') next++; } s = next; } kon_rmFieldRoute(field, index, c->routeData, size, flags & 1, (flags >> 1) & 1); if ((flags & 4) && !(flags & 1)) field->waitIndex = index; } int kon_rmFieldHost(kon_rmField_t *field, int code, int b, int c, int d, const char *text) { int n[8] = {0}, count, index; if (!field || !field->chars) return -1; count = parseInts(text, n, 8); if (count == 0) { n[0] = b; n[1] = c; n[2] = d; } switch (code) { case 123: if (field->selfSwitchSet && field->thisEvent > 0 && text && text[0] >= 'A' && text[0] <= 'D') { field->selfSwitchSet(field->map->id, field->thisEvent, text[0], n[1] == 0, field->user); kon_rmFieldRefresh(field); } return 0; case 201: { kon_rmTransfer_t *t = &field->transfer; t->mapId = n[0] == 1 ? variable(field, n[1]) : n[1]; t->x = n[0] == 1 ? variable(field, n[2]) : n[2]; t->y = n[0] == 1 ? variable(field, n[3]) : n[3]; t->dir = validDir(n[4]) ? n[4] : 0; t->fade = n[5] >= 0 && n[5] <= 2 ? n[5] : 0; if (t->mapId <= 0) return 0; field->transferPending = 1; return 1; } case 203: index = charIndex(field, n[0]); if (index < 0) return 0; if (n[1] == 2) { int other = charIndex(field, n[2]); if (other >= 0) { int ox = field->chars[other].x, oy = field->chars[other].y; locate(field, other, field->chars[index].x, field->chars[index].y, n[4]); locate(field, index, ox, oy, n[4]); } } else if (n[1] == 1) { locate(field, index, variable(field, n[2]), variable(field, n[3]), n[4]); } else { locate(field, index, n[2], n[3], n[4]); } return 0; case 205: index = charIndex(field, b); if (index < 0) return 0; hostRoute(field, index, c, text); return field->waitIndex == index ? 1 : 0; case 211: field->chars[KON_RM_PLAYER].transparent = b == 0; return 0; case 214: index = charIndex(field, 0); if (index > 0) { field->chars[index].erased = 1; clearPage(&field->chars[index]); } return 0; default: return -1; } } int kon_rmFieldBusy(const kon_rmField_t *field) { if (!field || !field->chars) return 0; if (field->transferPending) return 1; if (field->waitIndex >= 0 && field->waitIndex < field->count) { const kon_rmChar_t *c = &field->chars[field->waitIndex]; return c->active && c->routeForced; } return 0; } int kon_rmFieldTransfer(kon_rmField_t *field, kon_rmTransfer_t *transfer) { if (!field || !field->transferPending) return 0; field->transferPending = 0; if (transfer) *transfer = field->transfer; return 1; } /*** drawing ***/ int kon_rmCharX(const kon_rmChar_t *c) { int jump = 0; if (c->jumpCount > 0) jump = c->jumpDx * 256 * c->jumpCount / (c->jumpPeak * 2); return c->x * 256 - c->moveDx * c->remain - jump; } int kon_rmCharY(const kon_rmChar_t *c) { int jump = 0; if (c->jumpCount > 0) jump = c->jumpDy * 256 * c->jumpCount / (c->jumpPeak * 2); return c->y * 256 - c->moveDy * c->remain - jump; } int kon_rmCharJump(const kon_rmChar_t *c) { int d; if (c->jumpCount <= 0) return 0; d = c->jumpCount - c->jumpPeak; return (c->jumpPeak * c->jumpPeak - d * d) / 2; } int kon_rmCharMoving(const kon_rmChar_t *c) { return moving(c); }