From 1ead3ebe085df069eb8b7af37b2f765daa2e23c5 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 19 Dec 2004 16:39:51 +0000 Subject: [PATCH] =?UTF-8?q?http://eressea.upb.de/mantis/view.php=3Fid=3D32?= =?UTF-8?q?9=20http://eressea.upb.de/mantis/view.php=3Fid=3D285=20=20Insek?= =?UTF-8?q?ten=20k=C3=B6nnen=20Gletscher=20betreten.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Kompletter Umbau der movement-Routinen, wesentlich sauberer getrennt. Konstrukte wie A wird von B verfolgt, welcher C transportiert, und C wird von D verfolgt klappen, und kommend amit klar, das Einheiten zwishendurch stehenbleiben wegen fehlender Reichweite oder Gletscherproblemen. Wenn hier nicht mindestens ein Bug drin ist, wäre ich überrascht, obwohl ich es mit diversen konstruierten Testfällen in LUA ausprobiert habe. Ich teste das gleich mal mit Datenfiles. --- src/common/gamecode/monster.c | 30 +- src/common/kernel/battle.c | 2 +- src/common/kernel/border.h | 2 +- src/common/kernel/combatspells.c | 4 +- src/common/kernel/eressea.c | 126 ++-- src/common/kernel/eressea.h | 11 +- src/common/kernel/message.c | 1 + src/common/kernel/movement.c | 983 +++++++++++++++++-------------- src/common/kernel/movement.h | 2 +- src/common/kernel/reports.h | 2 +- src/common/kernel/spell.c | 26 +- src/eressea/lua/eressea.cpp | 12 + src/eressea/lua/region.cpp | 18 + src/res/messages.xml | 15 +- src/res/vinyambar-wdw.xml | 1 - src/scripts/samples.lua | 84 ++- src/todo.txt | 4 + 17 files changed, 756 insertions(+), 567 deletions(-) diff --git a/src/common/gamecode/monster.c b/src/common/gamecode/monster.c index a019c97a9..42a14fff1 100644 --- a/src/common/gamecode/monster.c +++ b/src/common/gamecode/monster.c @@ -915,29 +915,38 @@ void plan_monsters(void) { region *r; - unit *u; - attrib *ta; - faction *f = findfaction(MONSTER_FACTION); - if (!f) - return; + if (!f) return; f->lastorders = turn; for (r = regions; r; r = r->next) { + unit *u; for (u = r->units; u; u = u->next) { - region * tr = NULL; boolean is_moving = false; + attrib * ta; /* Ab hier nur noch Befehle für NPC-Einheiten. */ - if (u->faction->no != MONSTER_FACTION) continue; /* Monster bekommen jede Runde ein paar Tage Wahrnehmung dazu */ produceexp(u, SK_OBSERVATION, u->number); + /* Haben Drachen ihr Ziel erreicht? */ + ta = a_find(u->attribs, &at_targetregion); + if (ta) { + if (u->region == (region*)ta->data.v) { + a_remove(&u->attribs, ta); + set_order(&u->lastorder, parse_order(keywords[K_WAIT], u->faction->locale)); + free_order(u->lastorder); /* parse_order & set_order have each increased the refcount */ + } + else { + is_moving = true; + } + } + ta = a_find(u->attribs, &at_hate); - if (ta && is_waiting(u)) { + if (ta && !is_waiting(u)) { unit * tu = (unit *)ta->data.v; if (tu && tu->region==r) { sprintf(buf, "%s %s", locale_string(u->faction->locale, keywords[K_ATTACK]), itoa36(tu->no)); @@ -948,11 +957,6 @@ plan_monsters(void) } else a_remove(&u->attribs, ta); } - ta = a_find(u->attribs, &at_targetregion); - if (ta!=NULL) { - tr = (region *) ta->data.v; - if (tr != r) is_moving = true; - } if (!(fval(u, UFL_ISNEW)) && r->terrain != T_OCEAN) { /* Monster bewachen immer */ const char * cmd = locale_string(u->faction->locale, keywords[K_GUARD]); diff --git a/src/common/kernel/battle.c b/src/common/kernel/battle.c index df3b33bd1..654c1a757 100644 --- a/src/common/kernel/battle.c +++ b/src/common/kernel/battle.c @@ -2500,7 +2500,7 @@ aftermath(battle * b) fset(du, UFL_LONGACTION); leave(du->region, du); if (df->run.region) { - travel(du, df->run.region, 1, NULL); + run_to(du, df->run.region); df->run.region = du->region; } } else diff --git a/src/common/kernel/border.h b/src/common/kernel/border.h index 4a7d30293..7a6209add 100644 --- a/src/common/kernel/border.h +++ b/src/common/kernel/border.h @@ -78,7 +78,7 @@ extern "C" { /* is the border in a valid state, * or should it be erased at the end of this turn to save space? */ - void (*move)(const border *, struct unit * u, const struct region * from, const struct region * to); + struct region * (*move)(const border *, struct unit * u, struct region * from, struct region * to); /* executed when the units traverses this border */ struct border_type * next; /* for internal use only */ } border_type; diff --git a/src/common/kernel/combatspells.c b/src/common/kernel/combatspells.c index ab75df87d..967590ff8 100644 --- a/src/common/kernel/combatspells.c +++ b/src/common/kernel/combatspells.c @@ -1366,9 +1366,7 @@ sp_denyattack(fighter * fi, int level, double power, spell * sp) case K_ROUTE: init_tokens(mage->thisorder); skip_token(); - fi->run.region = movewhere(r, mage); - if (!fi->run.region) { - cmistake(mage, mage->thisorder, 71, MSG_MOVE); + if (movewhere(mage, getstrtoken(), mage->region, &fi->run.region)!=E_MOVE_OK) { fi->run.region = fleeregion(mage); } break; diff --git a/src/common/kernel/eressea.c b/src/common/kernel/eressea.c index 578fe6175..abd84fe31 100644 --- a/src/common/kernel/eressea.c +++ b/src/common/kernel/eressea.c @@ -1039,7 +1039,7 @@ cansee(const faction * f, const region * r, const unit * u, int modifier) #endif o = eff_skill(u2, SK_OBSERVATION, r); -#if NIGHTEYES +#ifdef NIGHTEYES if (u2->enchanted == SP_NIGHT_EYES && o < NIGHT_EYE_TALENT) o = NIGHT_EYE_TALENT; #endif @@ -1102,7 +1102,7 @@ cansee(faction * f, region * r, unit * u, int modifier) #endif o = eff_skill(u2, SK_OBSERVATION, r); -#if NIGHTEYES +#ifdef NIGHTEYES if (u2->enchanted == SP_NIGHT_EYES && o < NIGHT_EYE_TALENT) o = NIGHT_EYE_TALENT; #endif @@ -1133,17 +1133,19 @@ cansee_durchgezogen(const faction * f, const region * r, const unit * u, int mod else if (u->faction == f) cansee = true; else { - n = eff_stealth(u, r) - modifier; + if (getguard(u) || usiege(u) || u->building || u->ship) { + cansee = true; + } - for (u2 = r->units; u2; u2 = u2->next){ + n = eff_stealth(u, r) - modifier; + if (n<=0) { + cansee = true; + } + + for (u2 = r->units; !cansee && u2; u2 = u2->next){ if (u2->faction == f) { int o; - if (getguard(u) || usiege(u) || u->building || u->ship) { - cansee = true; - break; - } - #if NEWATSROI == 0 if (invisible(u) >= u->number && !get_item(u2, I_AMULET_OF_TRUE_SEEING)) @@ -1152,13 +1154,12 @@ cansee_durchgezogen(const faction * f, const region * r, const unit * u, int mod o = eff_skill(u2, SK_OBSERVATION, r); -#if NIGHTEYES +#ifdef NIGHTEYES if (u2->enchanted == SP_NIGHT_EYES && o < NIGHT_EYE_TALENT) o = NIGHT_EYE_TALENT; #endif if (o >= n) { cansee = true; - break; } } } @@ -2924,79 +2925,48 @@ findspecialdirection(const region *r, const char *token) return NULL; } -region * -movewhere(region * r, const unit *u) +message * +movement_error(unit * u, const char * token, order * ord, int error_code) { - direction_t d; - const char *token; - region * r2; + direction_t d; + switch (error_code) { + case E_MOVE_BLOCKED: + d = finddirection(token, u->faction->locale); + return msg_message("moveblocked", "unit direction", u, d); + case E_MOVE_NOREGION: + return msg_feedback(u, ord, "unknowndirection", "direction", token); + } + return NULL; +} - token = getstrtoken(); +int +movewhere(const unit *u, const char * token, region * r, region** resultp) +{ + direction_t d = finddirection(token, u->faction->locale); + region * r2; - d = finddirection(token, u->faction->locale); - if (d == D_PAUSE) - return r; + switch (d) { - if (d == NODIRECTION) - return findspecialdirection(r, token); + case D_PAUSE: + *resultp = r; + break; -#if 0 /* NOT here! make a temporary attribute for this and move it into travel() */ - if (is_cursed(r->attribs, C_REGCONF, 0)) { - if (rand()%100 < get_curseeffect(r->attribs, C_REGCONF, 0)) { - if(u->wants < 0) u->wants--; - else if(u->wants > 0) u->wants++; - else u->wants = rand()%2==0?1:-1; - } + case NODIRECTION: + r2 = findspecialdirection(r, token); + if (r2!=NULL) { + return E_MOVE_NOREGION; + } + *resultp = r2; + break; + + default: + r2 = rconnect(r, d); + if (r2==NULL || move_blocked(u, r, r2)) { + return E_MOVE_BLOCKED; + } + *resultp = r2; } - - if (u->ship && is_cursed(u->ship->attribs, C_DISORIENTATION, 0)) { - if (rand()%100 < get_curseeffect(r->attribs, C_DISORIENTATION, 0)) { - if(u->wants < 0) u->wants--; - else if(u->wants > 0) u->wants++; - else u->wants = rand()%20?1:-1; - } - } - d = (direction_t)((d + u->wants + MAXDIRECTIONS) % MAXDIRECTIONS); -#endif - - if (!rconnect(r, d)) { -#ifdef USE_CREATION - makeblock(r->x + delta_x[d], r->y + delta_y[d], 1); - log_error((("Region (%d,%d) hatte seine Nachbarn " - "(%d,%d) noch nicht generiert!\n", r->x, r->y, - r->x + delta_x[d], r->y + delta_y[d])); -#else - add_message(&u->faction->msgs, - msg_message("moveblocked", "unit direction", u, d)); - return NULL; -#endif - } - r2 = rconnect(r, d); - - if (!r2) { - log_error(("Region (%d,%d) hatte seine Nachbarn " - "(%d,%d) nicht gefunden!", r->x, r->y, - r->x + delta_x[d], r->y + delta_y[d])); - return 0; - } - - if (move_blocked(u, r, r2)) { - add_message(&u->faction->msgs, - msg_message("moveblocked", "unit direction", u, d)); - return NULL; - } - - /* r2 enthält nun die existierende Zielregion - ihre Nachbarn sollen - * auch schon alle existieren. Dies erleichtert das Umherschauen bei - * den Reports! */ - -#ifdef USE_CREATION - for (d = 0; d != MAXDIRECTIONS; d++) - if (!rconnect(r2, d)) - makeblock(r2->x + delta_x[d], r2->y + delta_y[d], 1); -#endif - - return r2; + return E_MOVE_OK; } boolean diff --git a/src/common/kernel/eressea.h b/src/common/kernel/eressea.h index 89e96f542..7b2912f28 100644 --- a/src/common/kernel/eressea.h +++ b/src/common/kernel/eressea.h @@ -709,8 +709,12 @@ enum { NORACE = (race_t) - 1 }; - -/* ---------- Regionen ----------------------------------------- */ +/* movewhere error codes */ +enum { + E_MOVE_OK = 0, /* possible to move */ + E_MOVE_NOREGION, /* no region exists in this direction */ + E_MOVE_BLOCKED /* cannot see this region, there is a blocking border. */ +}; /* Richtungen */ enum { @@ -1120,7 +1124,8 @@ extern int maxworkingpeasants(const struct region * r); extern int wage(const struct region *r, const struct unit *u, boolean img); extern int fwage(const struct region *r, const struct faction *f, boolean img); -extern struct region * movewhere(struct region * r, const struct unit *u); +extern int movewhere(const struct unit *u, const char * token, struct region * r, struct region** resultp); +extern struct message * movement_error(struct unit * u, const char * token, struct order * ord, int error_code); extern boolean move_blocked(const struct unit * u, const struct region *src, const struct region *dest); extern void add_income(struct unit * u, int type, int want, int qty); diff --git a/src/common/kernel/message.c b/src/common/kernel/message.c index 54474e21f..b255a94a7 100644 --- a/src/common/kernel/message.c +++ b/src/common/kernel/message.c @@ -399,6 +399,7 @@ void cmistake(const unit * u, struct order *ord, int mno, int mtype) { static char ebuf[20]; + unused(mtype); if (u->faction->no == MONSTER_FACTION) return; sprintf(ebuf, "error%d", mno); diff --git a/src/common/kernel/movement.c b/src/common/kernel/movement.c index 03bd37d3b..8356f61ac 100644 --- a/src/common/kernel/movement.c +++ b/src/common/kernel/movement.c @@ -61,9 +61,8 @@ #include #include -/* TODO: boder_type::move() must be able to change target (wisps) */ extern border_type bt_wisps; -/* ------------------------------------------------------------- */ +int * storms; typedef struct traveldir { int no; @@ -80,6 +79,31 @@ static attrib_type at_traveldir = { DEFAULT_READ }; +typedef struct follower { + struct follower * next; + unit * uf; + unit * ut; + region_list * route_end; +} follower; + +static void +get_followers(unit * target, region * r, region_list * route_end, follower ** followers) +{ + unit * uf; + for (uf=r->units;uf;uf=uf->next) { + if (fval(uf, UFL_FOLLOWING) && !fval(uf, UFL_LONGACTION)) { + const attrib * a = a_findc(uf->attribs, &at_follow); + if (a && a->data.v == target) { + follower * fnew = malloc(sizeof(follower)); + fnew->uf = uf; + fnew->ut = target; + fnew->route_end = route_end; + fnew->next = *followers; + *followers = fnew; + } + } + } +} static void shiptrail_init(attrib *a) @@ -279,7 +303,14 @@ walkingcapacity(const struct unit * u) return n; } -int +enum { + E_CANWALK_OK = 0, + E_CANWALK_TOOMANYHORSES, + E_CANWALK_TOOMANYCARTS, + E_CANWALK_TOOHEAVY +}; + +static int canwalk(unit * u) { int wagen, maxwagen; @@ -288,7 +319,7 @@ canwalk(unit * u) /* workaround: monsters are too stupid to drop items, therefore they have * infinite carrying capacity */ - if (u->faction->no == 0) return 0; + if (u->faction->no == 0) return E_CANWALK_OK; wagen = get_item(u, I_WAGON); pferde = get_item(u, I_HORSE); @@ -300,10 +331,10 @@ canwalk(unit * u) maxpferde = effskill(u, SK_RIDING) * u->number * 4 + u->number; if (pferde > maxpferde) - return 2; + return E_CANWALK_TOOMANYHORSES; if (walkingcapacity(u) - eff_weight(u) >= 0) - return 0; + return E_CANWALK_OK; /* Stimmt das Gewicht, impliziert dies hier, daß alle Wagen ohne * Zugpferde/-trolle als Fracht aufgeladen wurden: zu viele Pferde hat @@ -311,12 +342,12 @@ canwalk(unit * u) * als erlaubt. */ if (wagen > maxwagen) - return 3; + return E_CANWALK_TOOMANYCARTS; /* Es muß nicht zwingend an den Wagen liegen, aber egal... (man * könnte z.B. auch 8 Eisen abladen, damit ein weiterer Wagen als * Fracht draufpaßt) */ - return 1; + return E_CANWALK_TOOHEAVY; } boolean @@ -507,10 +538,10 @@ leave_trail(ship * sh, region * from, region_list *route) } static void -travel_route(const unit * u, region * r, region_list * route) +mark_travelthru(const unit * u, region * r, region_list * route, region_list * route_end) { /* kein travelthru in der letzten region! */ - while (route) { + while (route!=route_end) { travelthru(u, r); r = route->data; route = route->next; @@ -538,7 +569,7 @@ move_ship(ship * sh, region * from, region * to, region_list * route) leave_trail(sh, from, route); trail = true; } - if (route!=NULL) travel_route(u, from, route); + if (route!=NULL) mark_travelthru(u, from, route, NULL); if (from!=to) { u->ship = NULL; /* damit move_unit() kein leave() macht */ move_unit(u, to, ulist); @@ -881,21 +912,41 @@ cycle_route(unit *u, int gereist) free_order(u->lastorder); /* parse_order & set_order have each increased the refcount */ } +static boolean +transport(unit * ut, unit * u) +{ + order * ord; + + if (LongHunger(u)) { + return false; + } + + for (ord = ut->orders; ord; ord = ord->next) { + if (get_keyword(ord) == K_TRANSPORT) { + init_tokens(ord); + skip_token(); + if (getunit(ut->region, ut->faction) == u) { + return true; + } + } + } + return false; +} + static void -init_drive(void) +init_transportation(void) { region *r; - unit *u, *ut; for (r=regions; r; r=r->next) { + unit *u; + /* This is just a simple check for non-corresponding K_TRANSPORT/ * K_DRIVE. This is time consuming for an error check, but there * doesn't seem to be an easy way to speed this up. */ - - for(u=r->units; u; u=u->next) { + for (u=r->units; u; u=u->next) { if (get_keyword(u->thisorder) == K_DRIVE && !fval(u, UFL_LONGACTION) && !LongHunger(u)) { - boolean found = false; - order * ord; + unit * ut; init_tokens(u->thisorder); skip_token(); @@ -904,17 +955,7 @@ init_drive(void) cmistake(u, u->thisorder, 63, MSG_MOVE); continue; } - for (ord = ut->orders; ord; ord = ord->next) { - if (get_keyword(ord) == K_TRANSPORT) { - init_tokens(ord); - skip_token(); - if (getunit(r, ut->faction) == u) { - found = true; - break; - } - } - } - if (found == false) { + if (!transport(ut, u)) { if (cansee(u->faction, r, ut, 0)) { cmistake(u, u->thisorder, 286, MSG_MOVE); } else { @@ -925,15 +966,16 @@ init_drive(void) } /* This calculates the weights of all transported units and - * adds them to an internal counter which is used by travel() to - * calculate effective weight and movement. */ - + * adds them to an internal counter which is used by travel () to + * calculate effective weight and movement. */ for (u=r->units; u; u=u->next) { order * ord; int w = 0; for (ord = u->orders; ord; ord = ord->next) { if (get_keyword(ord) == K_TRANSPORT) { + unit * ut; + init_tokens(ord); skip_token(); ut = getunit(r, u->faction); @@ -949,43 +991,11 @@ init_drive(void) } } } - - if(w > 0) a_add(&u->attribs, a_new(&at_driveweight))->data.i = w; + if (w > 0) a_add(&u->attribs, a_new(&at_driveweight))->data.i = w; } } } -int *storms; - -#if 0 -/* in arbeit */ -#define ETRAVEL_NOTALLOWED 1 -#define ETRAVEL_NOSWIM 2 -#define ETRAVEL_NORIDE 4 -#define ETRAVEL_LEFTSHIP 8 - -int -cantravel(const unit *u, const region *from, const region * to, boolean (*allowed)(const struct region *, const struct region *)) -{ - /* basic allowed function */ - if (allowed && !allowed(from, to)) return ETRAVEL_NOTALLOWED; - if (u->ship && fval(u, UFL_OWNER) && rterrain(from)==T_OCEAN) { - /* wir sind auf einem schiff und auf dem ozean */ - if (rterrain(to)==T_OCEAN || (old_race(u->race) != RC_AQUARIAN && !dragon(u))) { - /* Drache oder Meermensch sind wir auch nicht */ - return ETRAVEL_NOSWIM; - } else if (get_item(u, I_HORSE) > 0) { - return ETRAVEL_NORIDE|ETRAVEL_NOSWIM; - } - } - if (!u->ship && leftship(u) && is_guarded(from, u, GUARD_LANDING)) { - /* An Land kein NACH wenn in dieser Runde Schiff VERLASSEN! */ - return ETRAVEL_LEFTSHIP; - } -} -#endif - - static boolean roadto(const region * r, direction_t dir) { @@ -1024,404 +1034,316 @@ direction_name(const region * from, const region * to, const struct locale * lan return NULL; } -static int -notify_followers(region * r, unit * target, const region_list * route) +static region_list * +cap_route(region * r, region_list * route, region_list * route_end, int speed) { - int followers = 0; - unit *up; + region * current = r; + int moves = speed; + region_list * iroute = route; + while (iroute!=route_end) { + region * next = iroute->data; + direction_t reldir = reldirection(current, next); - for (up=r->units;up;up=up->next) { - if (fval(up, UFL_FOLLOWING) && !fval(up, UFL_LONGACTION)) { - const attrib * a = a_findc(up->attribs, &at_follow); - if (a && a->data.v==target) { - /* wir basteln ihm ein NACH */ - const region_list * rlist = route; - region * from = r; + iroute = iroute->next; - strcpy(buf, locale_string(up->faction->locale, keywords[K_MOVE])); - while (rlist!=NULL) { - strcat(strcat(buf, " "), direction_name(from, rlist->data, up->faction->locale)); - from = rlist->data; - rlist = rlist->next; - } - set_order(&up->thisorder, parse_order(buf, up->faction->locale)); - free_order(up->thisorder); /* parse_order & set_order have each increased the refcount */ - ++followers; - } - } + /* adjust the range of the unit */ + if (roadto(current, reldir)) moves -= BP_ROAD; + else moves -= BP_NORMAL; + if (moves<0) break; + current = next; } - return followers; + return iroute; } -/** traveling without ships - * The travel unction returns the number of followers - in case it's non-null, - * the loop through the units in the region needs to be repeated - * to exceute the newly made MOVE commands of followers - */ -int -travel(unit * u, region * next, int flucht, region_list ** routep) +static void +make_route(unit * u, order * ord, region_list ** routep) { - region *first = u->region; - region *current = u->region; - int k, m = 0; - double dk; - unit *ut, *u2; - int gereist = 0; - region_list *route = NULL; - region_list **iroute = &route; - static boolean init = false; + region_list **iroute = routep; + region * current = u->region; + region * next = NULL; + const char * token = getstrtoken(); + int error = movewhere(u, token, current, &next); + + if (error!=E_MOVE_OK) { + message * msg = movement_error(u, token, ord, error); + if (msg!=NULL) { + add_message(&u->faction->msgs, msg); + msg_release(msg); + } + next = NULL; + } + + while (next!=NULL) { + border * b = get_borders(current, next); + direction_t reldir; + + if (current==next) { + /* PAUSE */ + break; + } + while (b!=NULL) { + if (b->type->move) { + region * rto = b->type->move(b, u, current, next); + if (rto!=next) { + /* the target region was changed (bt_wisps, for example). check the + * new target region for borders */ + next = rto; + b = get_borders(current, next); + continue; + } + } + b = b->next; + } + + reldir = reldirection(current, next); + + add_regionlist(iroute, next); + iroute = &(*iroute)->next; + + current = next; + token = getstrtoken(); + error = movewhere(u, token, current, &next); + if (error) { + message * msg = movement_error(u, token, ord, error); + if (msg!=NULL) { + add_message(&u->faction->msgs, msg); + msg_release(msg); + } + next = NULL; + break; + } + } +} + +/** calculate the speed of a unit + * + * zu Fuß reist man 1 Region, zu Pferd 2 Regionen. Mit Straßen reist + * man zu Fuß 2, mit Pferden 3 weit. + * + * Berechnet wird das mit BPs. Zu Fuß hat man 4 BPs, zu Pferd 6. + * Normalerweise verliert man 3 BP pro Region, bei Straßen nur 2 BP. + * Außerdem: Wenn Einheit transportiert, nur halbe BP + */ +static int +movement_speed(unit * u) +{ + int mp; static const curse_type * speed_ct; - int followers = 0; + static boolean init = false; + double dk = u->race->speed; - if (routep) *routep = NULL; - if (!init) { - init = true; - speed_ct = ct_find("speed"); - } - - /* tech: - * - * zu Fuß reist man 1 Region, zu Pferd 2 Regionen. Mit Straßen reist - * man zu Fuß 2, mit Pferden 3 weit. - * - * Berechnet wird das mit BPs. Zu Fuß hat man 4 BPs, zu Pferd 6. - * Normalerweise verliert man 3 BP pro Region, bei Straßen nur 2 BP. - * Außerdem: Wenn Einheit transportiert, nur halbe BP */ - - if (rterrain(current)==T_OCEAN - && !(u->race->flags & RCF_FLY) && rterrain(next) != T_OCEAN) - { /* Die Einheit kann nicht fliegen, ist im Ozean, und will an Land */ - if (!(u->race->flags & RCF_SWIM) && old_race(u->race) != RC_AQUARIAN) { - cmistake(u, u->thisorder, 44, MSG_MOVE); - return 0; - } else { - if (u->ship && get_item(u, I_HORSE) > 0) { - cmistake(u, u->thisorder, 67, MSG_MOVE); - return 0; - } - } - } else if (rterrain(current)!=T_OCEAN) { - /* An Land kein NACH wenn in dieser Runde Schiff VERLASSEN! */ - if (leftship(u) && is_guarded(current, u, GUARD_LANDING)) { - cmistake(u, u->thisorder, 70, MSG_MOVE); - return 0; - } - if (u->ship && !flucht && u->race->flags & RCF_SWIM) { - cmistake(u, u->thisorder, 143, MSG_MOVE); - return 0; - } - } else if (rterrain(next) == T_OCEAN && u->ship && fval(u->ship, SF_MOVED)) { - cmistake(u, u->thisorder, 13, MSG_MOVE); - return 0; - } - - switch (canwalk(u)) { - case 1: - cmistake(u, u->thisorder, 57, MSG_MOVE); - return 0; - case 2: - cmistake(u, u->thisorder, 56, MSG_MOVE); - return 0; - case 3: - cmistake(u, u->thisorder, 42, MSG_MOVE); - return 0; - } - - /* wir suchen so lange nach neuen Richtungen, wie es geht. Diese werden - * dann nacheinander ausgeführt. */ - - dk = u->race->speed; - - if (speed_ct) { - curse *c = get_curse(u->attribs, speed_ct); - if(c) { - int men = get_cursedmen(u, c); - dk *= 1.0 + (double)men/(double)u->number; - } - } - - switch(canride(u)) { - case 1: /* Pferd */ - k = (int)(dk*BP_RIDING); - break; - case 2: /* Einhorn */ - k = (int)(dk*BP_UNICORN); - break; - default: - { - int mp = 1; - - /* faction special */ - if(fspecial(u->faction, FS_QUICK)) - mp = BP_RIDING; - - /* Siebenmeilentee */ - if (get_effect(u, oldpotiontype[P_FAST]) >= u->number) { - mp *= 2; - change_effect(u, oldpotiontype[P_FAST], -u->number); - } - - /* unicorn in inventory */ - if (u->number <= get_item(u, I_FEENSTIEFEL)) - mp *= 2; - - /* Im Astralraum sind Tyb und Ill-Magier doppelt so schnell. - * Nicht kumulativ mit anderen Beschleunigungen! */ - if ( mp == 1 && getplane(next) == get_astralplane() && is_mage(u)) { - if(get_mage(u)->magietyp == M_ASTRAL - || get_mage(u)->magietyp == M_TRAUM) { - mp *= 2; - } - } - k = (int)(dk*mp*BP_WALKING); - } - break; - } - - switch(old_race(u->race)) { + /* dragons have a fixed speed, and no other effects work on them: */ + switch (old_race(u->race)) { case RC_DRAGON: case RC_WYRM: case RC_FIREDRAGON: case RC_BIRTHDAYDRAGON: case RC_PSEUDODRAGON: - k = BP_DRAGON; + return BP_DRAGON; } - /* die nächste Region, in die man wandert, wird durch movewhere() aus - * der letzten Region bestimmt. - * - * Anfangen tun wir bei r. r2 ist beim ersten Durchlauf schon gesetzt - * (Parameter!), das Ziel des Schrittes. m zählt die Schritte, k sind - * die noch möglichen Schritte, r3 die letzte gültige, befahrene Region. */ + if (!init) { + init = true; + speed_ct = ct_find("speed"); + } + if (speed_ct) { + curse *c = get_curse(u->attribs, speed_ct); + if (c!=NULL) { + int men = get_cursedmen(u, c); + dk *= 1.0 + (double)men/(double)u->number; + } + } - while (next) { - border * b = get_borders(current, next); + switch (canride(u)) { + + case 1: /* Pferd */ + mp = BP_RIDING; + break; + + case 2: /* Einhorn */ + mp = BP_UNICORN; + break; + + default: + mp = BP_WALKING; + /* faction special */ + if (fspecial(u->faction, FS_QUICK)) mp = BP_RIDING; + + /* Siebenmeilentee */ + if (get_effect(u, oldpotiontype[P_FAST]) >= u->number) { + mp *= 2; + change_effect(u, oldpotiontype[P_FAST], -u->number); + } + + /* unicorn in inventory */ + if (u->number <= get_item(u, I_FEENSTIEFEL)) { + mp *= 2; + } + + /* Im Astralraum sind Tyb und Ill-Magier doppelt so schnell. + * Nicht kumulativ mit anderen Beschleunigungen! */ + if (mp*dk <= BP_WALKING*u->race->speed && getplane(u->region) == get_astralplane() && is_mage(u)) { + sc_mage * mage = get_mage(u); + if (mage->magietyp == M_ASTRAL || mage->magietyp == M_TRAUM) { + mp *= 2; + } + } + break; + } + return (int)(dk*mp); +} + +enum { + TRAVEL_NORMAL, + TRAVEL_FOLLOWING, + TRAVEL_TRANSPORTED, + TRAVEL_RUNNING +}; + +static region_list * +travel_route(unit * u, region_list * route_begin, region_list * route_end, order * ord, int mode) +{ + region * r = u->region; + region * current = u->region; + region_list * iroute = route_begin; + int steps = 0; + + while (iroute && iroute!=route_end) { + region * next = iroute->data; direction_t reldir = reldirection(current, next); - while (b!=NULL) { - if (b->type==&bt_wisps) { - wall_data * wd = (wall_data*)b->data; - assert(reldir!=D_SPECIAL); - - if (wd->active) { - /* pick left and right region: */ - region * rl = rconnect(current, (direction_t)((reldir+MAXDIRECTIONS-1)%MAXDIRECTIONS)); - region * rr = rconnect(current, (direction_t)((reldir+1)%MAXDIRECTIONS)); - int j = rand() % 3; - if (j==0) break; - else if (j==1 && rl && landregion(rterrain(rl))==landregion(rterrain(next))) next = rl; - else if (j==2 && rr && landregion(rterrain(rr))==landregion(rterrain(next))) next = rr; - break; - } + /* check if we are caught by guarding units */ + if (iroute!=route_begin && mode!=TRAVEL_RUNNING && mode!=TRAVEL_TRANSPORTED) { + unit * wache = bewegung_blockiert_von(u, current); + if (wache!=NULL) { + ADDMSG(&u->faction->msgs, msg_message("moveblockedbyguard", + "unit region guard", u, current, wache)); + break; } - if (b->type->move) b->type->move(b, u, current, next); - b = b->next; } - if (current!=next) { /* !pause */ - if (roadto(current, reldir)) k-=BP_ROAD; - else k-=BP_NORMAL; - if (k<0) break; - if (reldir>=0 && move_blocked(u, current, next)) { - ADDMSG(&u->faction->msgs, msg_message("leavefail", - "unit region", u, next)); - } - if (!entrance_allowed(u, next)) { - ADDMSG(&u->faction->msgs, msg_message("regionowned", - "unit region target", u, current, next)); - break; - } - if (gereist) { - unit * wache = bewegung_blockiert_von(u, current); - if (wache!=NULL) { - ADDMSG(&u->faction->msgs, msg_message("moveblockedbyguard", - "unit region guard", u, current, wache)); + /* check movement from/to oceans. + * aquarian special, flying units, horses, the works */ + if ((u->race->flags & RCF_FLY) == 0) { + if (rterrain(current)==T_OCEAN && rterrain(next) != T_OCEAN) { + int moving = u->race->flags & (RCF_SWIM|RCF_WALK); + /* Die Einheit kann nicht fliegen, ist im Ozean, und will an Land */ + if (moving != (RCF_SWIM|RCF_WALK) && old_race(u->race) != RC_AQUARIAN) { + /* can't swim+walk and isn't an aquarian */ + if (ord!=NULL) cmistake(u, ord, 44, MSG_MOVE); break; } } - if (fval(u->race, RCF_ILLUSIONARY)) { - curse * c = get_curse(next->attribs, ct_find("antimagiczone")); - if (curse_active(c)) { - curse_changevigour(&next->attribs, c, - u->number); - add_message(&u->faction->msgs, new_message(u->faction, - "illusionantimagic%u:unit", u)); - destroy_unit(u); - if (routep) *routep = route; - else free_regionlist(route); - return 0; + if (rterrain(current)==T_OCEAN || rterrain(next) == T_OCEAN) { + /* trying to enter or exit ocean with horses, are we? */ + if (get_item(u, I_HORSE) > 0) { + /* tries to do it with horses */ + if (ord!=NULL) cmistake(u, ord, 67, MSG_MOVE); + break; } } + /* Ozeanfelder können nur von Einheiten mit Schwimmen und ohne - * Pferde betreten werden. Drachen können fliegen. */ - + * Pferde betreten werden. */ if (rterrain(next) == T_OCEAN && !canswim(u)) { - plane *pl = getplane(next); - if (reldirfaction->msgs, msg_message("detectoceandir", - "unit direction", u, reldir)); - } else { - ADDMSG(&u->faction->msgs, msg_message("detectocean", - "unit region", u, next)); - } + ADDMSG(&u->faction->msgs, msg_message("detectocean", + "unit region", u, next)); break; } + } - if (terrain[rterrain(next)].flags & FORBIDDEN_LAND) { - plane *pl = getplane(next); - if (reldirfaction->msgs, msg_message("detectforbiddendir", - "unit direction", u, reldir)); - } else { - ADDMSG(&u->faction->msgs, msg_message("detectforbidden", - "unit region", u, next)); - } - break; - } - - if (old_race(u->race)==RC_INSECT) { - if (r_insectstalled(next) && is_freezing(u)) { - ADDMSG(&u->faction->msgs, msg_message("detectforbidden", - "unit region", u, next)); - break; - } - } - add_regionlist(iroute, next); - iroute = &(*iroute)->next; - m++; - } else { + /* movement blocked by a wall */ + if (reldir>=0 && move_blocked(u, current, next)) { + ADDMSG(&u->faction->msgs, msg_message("leavefail", + "unit region", u, next)); break; } - current = next; - if(!flucht) { - ++gereist; - if (current==first) break; /* PAUSE beendet die Reise */ - next = movewhere(current, u); + /* region ownership only: region owned by enemies */ + if (!entrance_allowed(u, next)) { + ADDMSG(&u->faction->msgs, msg_message("regionowned", + "unit region target", u, current, next)); + break; } + + /* illusionary units disappear in antimagic zones */ + if (fval(u->race, RCF_ILLUSIONARY)) { + curse * c = get_curse(next->attribs, ct_find("antimagiczone")); + if (curse_active(c)) { + curse_changevigour(&next->attribs, c, - u->number); + ADDMSG(&u->faction->msgs, msg_message("illusionantimagic", "unit", u)); + set_number(u, 0); + break; + } + } + + /* terrain is marked as forbidden (curse, etc) */ + if (terrain[rterrain(next)].flags & FORBIDDEN_LAND) { + ADDMSG(&u->faction->msgs, msg_message("detectforbidden", + "unit region", u, next)); + break; + } + + /* unit is an insect and cannot move into a glacier */ + if (old_race(u->race)==RC_INSECT) { + if (r_insectstalled(next) && is_freezing(u)) { + ADDMSG(&u->faction->msgs, msg_message("detectforbidden", + "unit region", u, next)); + break; + } + } + + current = next; + iroute = iroute->next; + ++steps; } - if (gereist) { + if (iroute!=route_begin) { + /* the unit has moved at least one region */ int mode; - order * ord; + region_list *rlist = route_begin; + char * p = buf; + region * next = r; setguard(u, GUARD_NONE); - cycle_route(u, gereist); + cycle_route(u, steps); - buf[0] = 0; - - if (flucht == 1) - mode = 0; - else if (canride(u)) { + if (canride(u)) { mode = 1; produceexp(u, SK_RIDING, u->number); - } else + } else { mode = 2; + } /* TODO: Flucht mit 0, was mit transport? */ - /* Haben Drachen ihr Ziel erreicht? */ - if (u->faction->no==MONSTER_FACTION) { - attrib * ta = a_find(u->attribs, &at_targetregion); - if (ta && current == (region*)ta->data.v) { - a_remove(&u->attribs, ta); - set_order(&u->lastorder, parse_order(keywords[K_WAIT], u->faction->locale)); - free_order(u->lastorder); /* parse_order & set_order have each increased the refcount */ - } - } + /* Berichte über Durchreiseregionen */ - /* Über die letzte Region braucht man keinen Bericht */ - - m--; - if (m > 0) { - region_list *rlist = route; - char * p = buf; - - travelthru(u, first); - - while (rlist!=NULL) { - region * r = rlist->data; - - rlist = rlist->next; - if (rlist!=NULL) { - MSG(("travelthru_trail", "region", r), p, sizeof(buf) - (p-buf), - u->faction->locale, u->faction); - if (rlist->next!=NULL) { - if (rlist->next->data==current) scat(" und "); - else scat(", "); - } - p += strlen(p); + *p = 0; + while (rlist!=iroute) { + if (next!=u->region && next!=current) { + MSG(("travelthru_trail", "region", next), + p, sizeof(buf) - (p-buf), u->faction->locale, u->faction); + if (rlist->data!=current) { + if (rlist->next->data==current) scat(" und "); + else scat(", "); } - travelthru(u, r); + p += strlen(p); } + next = rlist->data; + rlist = rlist->next; } ADDMSG(&u->faction->msgs, msg_message("travel", - "unit mode start end regions", u, mode, first, current, strdup(buf))); + "unit mode start end regions", u, mode, r, current, strdup(buf))); - /* und jetzt noch die transportierten Einheiten verschieben */ - for (ord = u->orders; ord; ord = ord->next) { - if (get_keyword(ord) == K_TRANSPORT) { - init_tokens(ord); - skip_token(); - ut = getunit(first, u->faction); - if (ut) { - boolean found = false; - if (get_keyword(ut->thisorder) == K_DRIVE) { - if (!fval(ut, UFL_LONGACTION) && !LongHunger(ut)) { - init_tokens(ut->thisorder); - skip_token(); - u2 = getunit(first, ut->faction); - if (u2 == u) { - found = true; - add_message(&u->faction->msgs, new_message( - u->faction, "transport%u:unit%u:target%r:start%r:end", - u, ut, first, current)); - if (!(terrain[current->terrain].flags & WALK_INTO) && get_item(ut, I_HORSE)) { - cmistake(u, u->thisorder, 67, MSG_MOVE); - cmistake(ut, ut->thisorder, 67, MSG_MOVE); - continue; - } - if (can_survive(ut, current)) { - travel_route(ut, ut->region, route); - move_unit(ut, current, NULL); - if (fval(ut, UFL_FOLLOWED) && route!=NULL) { - followers += notify_followers(first, ut, route); - } - } else { - cmistake(u, u->thisorder, 287, MSG_MOVE); - cmistake(ut, ut->thisorder, 230, MSG_MOVE); - continue; - } - } - } - } - if (!found) { - if (cansee(u->faction, u->region, ut, 0)) { - cmistake(u, u->thisorder, 90, MSG_MOVE); - } else { - cmistake(u, u->thisorder, 63, MSG_MOVE); - } - } - } else { - if (ut) { - cmistake(u, u->thisorder, 63, MSG_MOVE); - } else { - cmistake(u, u->thisorder, 99, MSG_MOVE); - } - } - } - } + mark_travelthru(u, r, route_begin, iroute); move_unit(u, current, NULL); + + /* make orders for the followers */ } - else if (flucht) { - move_unit(u, current, NULL); - } - set_order(&u->thisorder, NULL); fset(u, UFL_LONGACTION); setguard(u, GUARD_NONE); - - if (fval(u, UFL_FOLLOWING)) caught_target(current, u); - if (routep) *routep = route; - else free_regionlist(route); - return followers; + assert(u->region==current); + return iroute; } static boolean @@ -1514,18 +1436,32 @@ check_takeoff(ship *sh, region *from, region *to) return true; } -static region_list * -sail(unit * u, region * next_point, boolean move_on_land) +static void +sail(unit * u, order * ord, boolean move_on_land, region_list **routep) { region *starting_point = u->region; region *current_point, *last_point; int k, step = 0; - region_list *route = NULL; - region_list **iroute = &route; + region_list **iroute = routep; ship * sh = u->ship; faction * f = u->faction; + region * next_point = NULL; + int error; + const char * token = getstrtoken(); - if (!ship_ready(starting_point, u)) return NULL; + if (routep) *routep = NULL; + + error = movewhere(u, token, starting_point, &next_point); + if (error) { + message * msg = movement_error(u, token, ord, error); + if (msg!=NULL) { + add_message(&u->faction->msgs, msg); + msg_release(msg); + } + return; + } + + if (!ship_ready(starting_point, u)) return; /* Wir suchen so lange nach neuen Richtungen, wie es geht. Diese werden * dann nacheinander ausgeführt. */ @@ -1535,7 +1471,7 @@ sail(unit * u, region * next_point, boolean move_on_land) last_point = starting_point; current_point = starting_point; - /* die nächste Region, in die man segelt, wird durch movewhere() aus der + /* die nächste Region, in die man segelt, wird durch movewhere () aus der * letzten Region bestimmt. * * Anfangen tun wir bei starting_point. next_point ist beim ersten @@ -1543,6 +1479,8 @@ sail(unit * u, region * next_point, boolean move_on_land) * befahrene Region. */ while (current_point!=next_point && step < k && next_point) { + const char * token; + int error; terrain_t tthis = rterrain(current_point); /* these values need to be updated if next_point changes (due to storms): */ terrain_t tnext = rterrain(next_point); @@ -1627,7 +1565,7 @@ sail(unit * u, region * next_point, boolean move_on_land) } else { if (check_takeoff(sh, current_point, next_point) == false) { /* Schiff kann nicht ablegen */ - cmistake(u, u->thisorder, 182, MSG_MOVE); + cmistake(u, ord, 182, MSG_MOVE); break; } } @@ -1643,7 +1581,7 @@ sail(unit * u, region * next_point, boolean move_on_land) } if (d==MAXDIRECTIONS) { /* Schiff kann nicht aufs offene Meer */ - cmistake(u, u->thisorder, 249, MSG_MOVE); + cmistake(u, ord, 249, MSG_MOVE); break; } } @@ -1674,15 +1612,27 @@ sail(unit * u, region * next_point, boolean move_on_land) /* Falls kein Problem, eines weiter ziehen */ fset(sh, SF_MOVED); - add_regionlist(iroute, next_point); - iroute = &(*iroute)->next; + if (iroute) { + add_regionlist(iroute, next_point); + iroute = &(*iroute)->next; + } step++; last_point = current_point; current_point = next_point; if (rterrain(current_point) != T_OCEAN && !is_cursed(sh->attribs, C_SHIP_FLYING, 0)) break; - next_point = movewhere(current_point, u); + token = getstrtoken(); + error = movewhere(u, token, current_point, &next_point); + if (error) { + message * msg = movement_error(u, token, ord, error); + if (msg!=NULL) { + add_message(&u->faction->msgs, msg); + msg_release(msg); + } + next_point=current_point; + break; + } } if (sh->damage>=sh->size * DAMAGE_SCALE) { @@ -1723,7 +1673,7 @@ sail(unit * u, region * next_point, boolean move_on_land) /* Verfolgungen melden */ if (fval(u, UFL_FOLLOWING)) caught_target(current_point, u); - sh = move_ship(sh, starting_point, current_point, route); + sh = move_ship(sh, starting_point, current_point, *routep); /* Hafengebühren ? */ @@ -1784,7 +1734,6 @@ sail(unit * u, region * next_point, boolean move_on_land) } } } - return route; } unit * @@ -1809,32 +1758,164 @@ get_captain(const ship * sh) * * the token parser needs to be initialized before calling this function! */ -static int -move(unit * u, boolean move_on_land) + +/** fleeing units use this function +*/ +void +run_to(unit * u, region * to) +{ + region_list * route = NULL; + add_regionlist(&route, to); + travel_route(u, route, NULL, NULL, TRAVEL_RUNNING); + /* weder transport noch follow */ +} + +static region_list * +travel_i(unit * u, region_list * route_begin, region_list * route_end, order * ord, int mode, follower ** followers) { region * r = u->region; - region_list * route = NULL; - region * r2 = movewhere(r, u); - int followers = 0; - if (r2==NULL) { - cmistake(u, u->thisorder, 71, MSG_MOVE); - return 0; + switch (canwalk(u)) { + case E_CANWALK_TOOHEAVY: + cmistake(u, ord, 57, MSG_MOVE); + return route_begin; + case E_CANWALK_TOOMANYHORSES: + cmistake(u, ord, 56, MSG_MOVE); + return route_begin; + case E_CANWALK_TOOMANYCARTS: + cmistake(u, ord, 42, MSG_MOVE); + return route_begin; } - else if (u->ship && fval(u, UFL_OWNER)) { - route = sail(u, r2, move_on_land); + route_end = cap_route(r, route_begin, route_end, movement_speed(u)); + + route_end = travel_route(u, route_begin, route_end, ord, mode); + get_followers(u, r, route_end, followers); + + /* transportation */ + for (ord = u->orders; ord; ord = ord->next) { + unit * ut; + + if (get_keyword(ord) != K_TRANSPORT) continue; + + init_tokens(ord); + skip_token(); + ut = getunit(r, u->faction); + if (ut!=NULL) { + boolean found = false; + if (get_keyword(ut->thisorder) == K_DRIVE) { + if (!fval(ut, UFL_LONGACTION) && !LongHunger(ut)) { + init_tokens(ut->thisorder); + skip_token(); + if (getunit(u->region, ut->faction) == u) { + region_list * route_to = travel_route(ut, route_begin, route_end, ord, TRAVEL_TRANSPORTED); + + if (route_to!=route_begin) { + get_followers(ut, r, route_to, followers); + } + ADDMSG(&ut->faction->msgs, msg_message("transport", + "unit target start end", u, ut, r, ut->region)); + found = true; + } + } + if (!found) { + if (cansee(u->faction, u->region, ut, 0)) { + cmistake(u, ord, 90, MSG_MOVE); + } else { + cmistake(u, ord, 63, MSG_MOVE); + } + } + } else { + cmistake(u, ord, 99, MSG_MOVE); + } + } else { + cmistake(u, ord, 63, MSG_MOVE); + } + } + return route_end; +} + +/** traveling without ships + * walking, flying or riding units use this function + */ +static void +travel(unit * u, region_list ** routep) +{ + static boolean init = false; + region * r = u->region; + region_list * route_end; + region_list * route_begin = NULL; + follower * followers = NULL; + + if (routep) *routep = NULL; + + /* a few pre-checks that need not be done for each step: */ + if (rterrain(r)!=T_OCEAN) { + /* An Land kein NACH wenn in dieser Runde Schiff VERLASSEN! */ + if (leftship(u) && is_guarded(r, u, GUARD_LANDING)) { + cmistake(u, u->thisorder, 70, MSG_MOVE); + return; + } + if (u->ship && u->race->flags & RCF_SWIM) { + cmistake(u, u->thisorder, 143, MSG_MOVE); + return; + } + } + else if (u->ship && fval(u->ship, SF_MOVED)) { + /* die Einheit ist auf einem Schiff, das sich bereits bewegt hat */ + cmistake(u, u->thisorder, 13, MSG_MOVE); + return; + } + + make_route(u, u->thisorder, routep); + route_begin = *routep; + + /* und ab die post: */ + route_end = travel_i(u, route_begin, NULL, u->thisorder, TRAVEL_NORMAL, &followers); + + /* followers */ + while (followers!=NULL) { + follower * fnext = followers->next; + unit * uf = followers->uf; + unit * ut = followers->ut; + region_list * route_end = followers->route_end; + + free(followers); + followers = fnext; + + if (uf->region==r) { + order * follow_order; + char str[32]; + + /* construct an order */ + sprintf(str, "%s %s %s", + locale_string(uf->faction->locale, keywords[K_FOLLOW]), + locale_string(uf->faction->locale, parameters[P_UNIT]), + itoa36(ut->no)); + follow_order = parse_order(str, uf->faction->locale); + + route_end = travel_i(uf, route_begin, route_end, follow_order, TRAVEL_FOLLOWING, &followers); + caught_target(uf->region, uf); + free_order(follow_order); + } + } + +} + +static void +move(unit * u, boolean move_on_land) +{ + region_list * route = NULL; + + if (u->ship && fval(u, UFL_OWNER)) { + sail(u, u->thisorder, move_on_land, &route); } else { - followers += travel(u, r2, 0, &route); + travel(u, &route); } fset(u, UFL_LONGACTION); set_order(&u->thisorder, NULL); - if (fval(u, UFL_FOLLOWED) && route!=NULL) { - return followers + notify_followers(r, u, route); - } if (route!=NULL) free_regionlist(route); - return followers; } typedef struct piracy_data { @@ -1873,7 +1954,7 @@ mk_piracy(const faction * f, direction_t target_dir) return a; } -static int +static void piracy_cmd(unit *u, struct order * ord) { region *r = u->region; @@ -1888,12 +1969,12 @@ piracy_cmd(unit *u, struct order * ord) if (!sh) { cmistake(u, ord, 144, MSG_MOVE); - return 0; + return; } if (!fval(u, UFL_OWNER)) { cmistake(u, ord, 46, MSG_MOVE); - return 0; + return; } /* Feststellen, ob schon ein anderer alliierter Pirat ein @@ -1962,7 +2043,7 @@ piracy_cmd(unit *u, struct order * ord) if (target_dir == NODIRECTION) { add_message(&u->faction->msgs, new_message(u->faction, "piratenovictim%h:ship%r:region", sh, r)); - return 0; + return; } /* Meldung generieren */ @@ -1978,7 +2059,7 @@ piracy_cmd(unit *u, struct order * ord) /* Bewegung ausführen */ init_tokens(u->thisorder); skip_token(); - return move(u, true); + move(u, true); } static void @@ -2079,10 +2160,7 @@ hunt(unit *u) /* NACH ignorieren und Parsing initialisieren. */ igetstrtoken(command); /* NACH ausführen */ - if (move(u, false)!=0) { - /* niemand sollte auf einen kapitän direkt ein folge haben, oder? */ - assert(1==0); - } + move(u, false); fset(u, UFL_LONGACTION); /* Von Hand setzen, um Endlosschleife zu vermeiden, wenn Verfolgung nicht erfolgreich */ return 1; /* true -> Einheitenliste von vorne durchgehen */ } @@ -2265,7 +2343,7 @@ movement(void) int ships; /* Initialize the additional encumbrance by transported units */ - init_drive(); + init_transportation(); /* Move ships in last phase, others first * This is to make sure you can't land someplace and then get off the ship @@ -2275,7 +2353,6 @@ movement(void) region * r = regions; while (r!=NULL) { unit ** up = &r->units; - boolean repeat = false; while (*up) { unit *u = *up; @@ -2295,13 +2372,13 @@ movement(void) if (u->ship && fval(u, UFL_OWNER)) { init_tokens(u->thisorder); skip_token(); - if (move(u, false)!=0) repeat = true; + move(u, false); } } else { if (u->ship==NULL || !fval(u, UFL_OWNER)) { init_tokens(u->thisorder); skip_token(); - if (move(u, false)!=0) repeat = true; + move(u, false); } } } @@ -2311,19 +2388,17 @@ movement(void) /* not moved, use next unit */ up = &u->next; } else if (*up && (*up)->region!=r) { - /* moved the previous unit along with u (units on ships, for example) - * must start from the beginning again */ + /* moved the upcoming unit along with u (units on ships or followers, + * for example). must start from the beginning again */ up = &r->units; } /* else *up is already the next unit */ } - if (!repeat) { - if (ships==0) { - /* Abtreiben von beschädigten, unterbemannten, überladenen Schiffen */ - drifting_ships(r); - } - r = r->next; + if (ships==0) { + /* Abtreiben von beschädigten, unterbemannten, überladenen Schiffen */ + drifting_ships(r); } + r = r->next; } } diff --git a/src/common/kernel/movement.h b/src/common/kernel/movement.h index 59bc8eac0..6cbc57ca5 100644 --- a/src/common/kernel/movement.h +++ b/src/common/kernel/movement.h @@ -51,7 +51,7 @@ extern int personcapacity(const struct unit *u); extern direction_t getdirection(const struct locale *); extern void movement(void); -extern int travel(struct unit * u, struct region * r2, int flucht, struct region_list** routep); +extern void run_to(struct unit * u, struct region * to); extern struct unit *is_guarded(struct region * r, struct unit * u, unsigned int mask); extern int enoughsailors(const struct ship * sh, const struct region * r); extern boolean canswim(struct unit *u); diff --git a/src/common/kernel/reports.h b/src/common/kernel/reports.h index ecb0d8b58..b438f6503 100644 --- a/src/common/kernel/reports.h +++ b/src/common/kernel/reports.h @@ -92,7 +92,7 @@ extern char **seasonnames; extern char **weeknames; extern char **monthnames; extern int *month_season; -extern int *storms; +extern int *storms; /* in movement.c */ extern char *agename; extern int seasons; extern int weeks_per_month; diff --git a/src/common/kernel/spell.c b/src/common/kernel/spell.c index 5eb333714..867463706 100644 --- a/src/common/kernel/spell.c +++ b/src/common/kernel/spell.c @@ -2980,8 +2980,8 @@ wall_write(const border * b, FILE * f) fprintf(f, "%d %d ", fd->mage?fd->mage->no:0, fd->force); } -static void -wall_move(const border * b, struct unit * u, const struct region * from, const struct region * to) +static region * +wall_move(const border * b, struct unit * u, struct region * from, struct region * to) { wall_data * fd = (wall_data*)b->data; int hp = dice(3, fd->force) * u->number; @@ -2999,7 +2999,7 @@ wall_move(const border * b, struct unit * u, const struct region * from, const s } } unused(from); - unused(to); + return to; } border_type bt_firewall = { @@ -3093,6 +3093,24 @@ wisps_name(const border * b, const region * r, const faction * f, int gflags) return "Irrlichter"; } +static region * +wisps_move(const border * b, struct unit * u, struct region * from, struct region * next) +{ + direction_t reldir = reldirection(from, next); + wall_data * wd = (wall_data*)b->data; + assert(reldir!=D_SPECIAL); + + if (wd->active) { + /* pick left and right region: */ + region * rl = rconnect(from, (direction_t)((reldir+MAXDIRECTIONS-1)%MAXDIRECTIONS)); + region * rr = rconnect(from, (direction_t)((reldir+1)%MAXDIRECTIONS)); + int j = rand() % 3; + if (j==1 && rl && landregion(rterrain(rl))==landregion(rterrain(next))) return rl; + if (j==2 && rr && landregion(rterrain(rr))==landregion(rterrain(next))) return rr; + } + return next; +} + border_type bt_wisps = { "wisps", b_transparent, /* transparent */ @@ -3105,6 +3123,8 @@ border_type bt_wisps = { b_rvisible, /* rvisible */ b_fvisible, /* fvisible */ b_uvisible, /* uvisible */ + NULL, /* visible */ + wisps_move }; static int diff --git a/src/eressea/lua/eressea.cpp b/src/eressea/lua/eressea.cpp index 3fd23a068..15f3cf34f 100644 --- a/src/eressea/lua/eressea.cpp +++ b/src/eressea/lua/eressea.cpp @@ -151,6 +151,15 @@ error_callback(lua_State * L) } #endif +static int +get_direction(const char * name) +{ + for (int i=0;i!=MAXDIRECTIONS;++i) { + if (strcasecmp(directions[i], name)==0) return i; + } + return NODIRECTION; +} + void bind_eressea(lua_State * L) { @@ -171,6 +180,9 @@ bind_eressea(lua_State * L) def("plan_monsters", &lua_planmonsters), def("set_brain", &race_setscript), + /* string to enum */ + def("direction", &get_direction), + /* localization: */ def("set_string", &lua_setstring), def("get_string", &lua_getstring), diff --git a/src/eressea/lua/region.cpp b/src/eressea/lua/region.cpp index 529df8db6..c301cec88 100644 --- a/src/eressea/lua/region.cpp +++ b/src/eressea/lua/region.cpp @@ -106,6 +106,19 @@ region_setflag(region& r, int bit, bool set) else r.flags &= ~(1< - "$unit($unit) in $region($region): '$order($command)' - Kann die zu transportierende Einheit nicht finden." - "$unit($unit) in $region($region): '$order($command)' - Could not find the unit to be transported." - "$unit($unit) in $region($region): '$order($command)' - Could not find the unit to be transported." + "$unit($unit) in $region($region): '$order($command)' - Die Einheit will nicht transportiert werden." + "$unit($unit) in $region($region): '$order($command)' - The unit doen not want to be transported." + "$unit($unit) in $region($region): '$order($command)' - The unit doen not want to be transported." @@ -4755,15 +4755,16 @@ "$unit($unit) in $region($region): '$order($command)' - The target-unit did not contact us." "$unit($unit) in $region($region): '$order($command)' - The target-unit did not contact us." - + + - "$unit($unit) in $region($region): '$order($command)' - Die Richtung wurde nicht erkannt." - "$unit($unit) in $region($region): '$order($command)' - Direction could not be recognized." - "$unit($unit) in $region($region): '$order($command)' - Direction could not be recognized." + "$unit($unit) in $region($region): '$order($command)' - Die Richtung '$dirname' wurde nicht erkannt." + "$unit($unit) in $region($region): '$order($command)' - Direction '$dirname' was not recognized." + "$unit($unit) in $region($region): '$order($command)' - Direction '$dirname' was not recognized." diff --git a/src/res/vinyambar-wdw.xml b/src/res/vinyambar-wdw.xml index 2c806fd7e..8a9452149 100644 --- a/src/res/vinyambar-wdw.xml +++ b/src/res/vinyambar-wdw.xml @@ -3,7 +3,6 @@ - diff --git a/src/scripts/samples.lua b/src/scripts/samples.lua index 06d8bb05a..df539be66 100644 --- a/src/scripts/samples.lua +++ b/src/scripts/samples.lua @@ -1,3 +1,84 @@ +function test_movement() + west = direction("west") + east = direction("east") + + r0 = terraform(0, 0, "plain") + r1 = terraform(1, 0, "desert") + r2 = terraform(2, 0, "glacier") + r3 = terraform(3, 0, "plain") + r4 = terraform(4, 0, "glacier") + + r0:set_road(east, 1.0) + r1:set_road(west, 1.0) + r1:set_road(east, 1.0) + r2:set_road(west, 1.0) + r2:set_road(east, 1.0) + r3:set_road(west, 1.0) + r3:set_road(east, 1.0) + r4:set_road(west, 1.0) + + orcs = add_faction("enno@eressea.de", "orc", "de") + orcs.age = 20 + + orc = add_unit(orcs, r0) + orc.number = 10 + orc:add_item("money", orc.number*10) + orc:add_item("horse", orc.number*3) + orc:set_skill("sk_riding", 10) + + bugs = add_faction("enno@eressea.de", "insect", "de") + bugs.age = 20 + + bug = add_unit(bugs, r0) + bug.number = 1 + bug:add_item("money", bug.number*10) + + orc:clear_orders() + orc:add_order("NUMMER PARTEI orcs") + orc:add_order("NUMMER EINHEIT orc") + orc:add_order("BENENNE EINHEIT Orks") + orc:add_order("ROUTE O O O P P O W W W W") + orc:add_order("GIB 0 ALLES Steine") + orc:add_order("GIB 0 ALLES Holz") + orc:add_order("TRANSPORTIEREN " .. itoa36(bug.id)) + + bug:clear_orders() + bug:add_order("NUMMER PARTEI bugs") + bug:add_order("NUMMER EINHEIT bug") + bug:add_order("BENENNE EINHEIT Käfer") + bug:add_order("GIB 0 ALLES Steine") + bug:add_order("GIB 0 ALLES Holz") + bug:add_order("FAHREN " .. itoa36(orc.id)) + + u = add_unit(orcs, r0) + u.number = 1 + u:add_item("horse", u.number*3) + u:add_item("money", u.number*10) + u:set_skill("sk_riding", 10) + u:set_skill("sk_stealth", 2) + u:clear_orders() + u:add_order("FOLGEN EINHEIT " .. itoa36(bug.id)) + u:add_order("NACH W") + u:add_order("NUMMER EINHEIT foLg") + u:add_order("BENENNE EINHEIT Verfolger") + + u2 = add_unit(orcs, r0) + u2.number = 1 + u2:add_item("horse", u2.number*3) + u2:add_item("money", u.number*10) + u2:set_skill("sk_riding", 10) + u2:set_skill("sk_stealth", 2) + u2:clear_orders() + u2:add_order("FOLGEN EINHEIT nix") + u2:add_order("NUMMER EINHEIT Last") + u2:add_order("BENENNE EINHEIT Verfolger-Verfolger") + + + process_orders() + write_reports() +end + + function test_handler() local function msg_handler(u, evt) @@ -221,7 +302,8 @@ function test_fail() print(f) end -test_fail() +test_movement() +-- test_fail() -- test_handler() -- test_parser() -- test_monsters() diff --git a/src/todo.txt b/src/todo.txt index 9e5d07e85..9cb5deaf2 100644 --- a/src/todo.txt +++ b/src/todo.txt @@ -1,3 +1,7 @@ +parteien- oder gruppenweises aftermath-markieren. + +XUL for Eressea + weights in item descriptions [11:01] am besten wäre, wenn mailit nur die optionen, nicht das ganze script enthielte