#ifdef _MSC_VER #include #endif #include "spy.h" #include "guard.h" #include "laws.h" #include "move.h" #include "reports.h" #include "study.h" /* kernel includes */ #include #include #include #include #include #include #include #include #include #include #include /* attributes includes */ #include #include #include /* util includes */ #include #include #include #include #include #include /* libc includes */ #include #include #include #include #include #include #include /* in spy steht der Unterschied zwischen Wahrnehmung des Opfers und * Spionage des Spions */ void spy_message(int spy, const unit * u, const unit * target) { char status[32]; sbstring sbs; sbs_init(&sbs, status, sizeof(status)); report_status(u, u->faction->locale, &sbs); ADDMSG(&u->faction->msgs, msg_message("spyreport", "spy target status", u, target, status)); if (spy > 20) { magic_t mtype = unit_get_magic(target); /* for mages, spells and magic school */ if (mtype != M_GRAY) { ADDMSG(&u->faction->msgs, msg_message("spyreport_mage", "spy target type", u, target, magic_school[mtype])); } } if (spy > 6) { faction *fv = visible_faction(u->faction, target); if (fv && fv != target->faction) { /* true faction */ ADDMSG(&u->faction->msgs, msg_message("spyreport_faction", "spy target faction", u, target, target->faction)); add_seen_faction(u->faction, target->faction); } } if (spy > 0) { int first = 1; int found = 0; skill *sv; char buf[4096]; buf[0] = 0; for (sv = target->skills; sv != target->skills + target->skill_size; ++sv) { if (sv->level > 0) { found++; if (first == 1) { first = 0; } else { str_strlcat(buf, ", ", sizeof(buf)); } str_strlcat(buf, (const char *)skillname((skill_t)sv->id, u->faction->locale), sizeof(buf)); str_strlcat(buf, " ", sizeof(buf)); str_strlcat(buf, itoa10(eff_skill(target, sv, target->region)), sizeof(buf)); } } if (found) { ADDMSG(&u->faction->msgs, msg_message("spyreport_skills", "spy target skills", u, target, buf)); } if (target->items) { ADDMSG(&u->faction->msgs, msg_message("spyreport_items", "spy target items", u, target, target->items)); } } } int spy_cmd(unit * u, struct order *ord) { unit *target; int spy, observe; double spychance, observechance; region *r = u->region; init_order(ord, NULL); getunit(r, u->faction, &target); if (!target) { ADDMSG(&u->faction->msgs, msg_feedback(u, u->thisorder, "feedback_unit_not_found", "")); return 0; } if (effskill(u, SK_SPY, NULL) < 1) { cmistake(u, u->thisorder, 39, MSG_EVENT); return 0; } /* Die Grundchance fuer einen erfolgreichen Spionage-Versuch ist 10%. * Fuer jeden Talentpunkt, den das Spionagetalent das Tarnungstalent * des Opfers uebersteigt, erhoeht sich dieses um 5%*/ spy = effskill(u, SK_SPY, NULL) - effskill(target, SK_STEALTH, r); spychance = 0.1 + fmax(spy * 0.05, 0.0); if (chance(spychance)) { produceexp(u, SK_SPY, u->number); spy_message(spy, u, target); } else { ADDMSG(&u->faction->msgs, msg_message("spyfail", "spy target", u, target)); } /* der Spion kann identifiziert werden, wenn das Opfer bessere * Wahrnehmung als das Ziel Tarnung + Spionage/2 hat */ observe = effskill(target, SK_PERCEPTION, r) - (effskill(u, SK_STEALTH, NULL) + effskill(u, SK_SPY, NULL) / 2); if (invisible(u, target) >= u->number) { if (observe > 0) observe = 0; } /* Anschliessend wird - unabhaengig vom Erfolg - gewuerfelt, ob der * Spionageversuch bemerkt wurde. Die Wahrscheinlich dafuer ist (100 - * SpionageSpion*5 + WahrnehmungOpfer*2)%. */ observechance = 1.0 - (effskill(u, SK_SPY, NULL) * 0.05) + (effskill(target, SK_PERCEPTION, NULL) * 0.02); if (chance(observechance)) { ADDMSG(&target->faction->msgs, msg_message("spydetect", "spy target", observe > 0 ? u : NULL, target)); } return 0; } static bool can_set_factionstealth(const unit * u, const faction * f) { region *lastr = NULL; /* for all units mu of our faction, check all the units in the region * they are in, if their visible faction is f, it's ok. use lastr to * avoid testing the same region twice in a row. */ unit *mu = u->faction->units; while (mu != NULL) { if (mu->number && mu->region != lastr) { unit *ru = mu->region->units; lastr = mu->region; while (ru != NULL) { if (ru->number) { faction *fv = visible_faction(f, ru); if (fv == f) { if (cansee(f, lastr, ru, 0)) return true; } } ru = ru->next; } } mu = mu->nextF; } return true; } void set_factionstealth(unit *u, faction *f) { attrib *a = a_find(u->attribs, &at_otherfaction); if (!a) a = a_add(&u->attribs, make_otherfaction(f)); else a->data.v = f; } static void stealth_race(unit *u, const char *s) { const race *trace; trace = findrace(s, u->faction->locale); if (trace) { /* demons can cloak as other player-races */ if (u_race(u) == get_race(RC_DAEMON)) { if (playerrace(trace)) { u->irace = trace; } } /* Singdrachen können sich nur als Drachen tarnen */ else if (u_race(u) == get_race(RC_SONGDRAGON) || u_race(u) == get_race(RC_BIRTHDAYDRAGON)) { if (trace == get_race(RC_SONGDRAGON) || trace == get_race(RC_FIREDRAGON) || trace == get_race(RC_DRAGON) || trace == get_race(RC_WYRM)) { u->irace = trace; } } /* Schablonen können sich als alles mögliche tarnen */ if (u_race(u)->flags & RCF_SHAPESHIFT) { u->irace = trace; set_racename(&u->attribs, NULL); } } else { if (u_race(u)->flags & RCF_SHAPESHIFT) { set_racename(&u->attribs, s); } } } int setstealth_cmd(unit * u, struct order *ord) { char token[64]; const char *s; init_order(ord, NULL); s = gettoken(token, sizeof(token)); /* Tarne ohne Parameter: Setzt maximale Tarnung */ if (s == NULL || *s == 0) { u_seteffstealth(u, -1); } else if (isdigit(*(const unsigned char *)s)) { /* Tarnungslevel setzen */ int level = atoi((const char *)s); if (level > effskill(u, SK_STEALTH, NULL)) { ADDMSG(&u->faction->msgs, msg_feedback(u, ord, "error_lowstealth", "")); } else { u_seteffstealth(u, level); } } else { switch (findparam(s, u->faction->locale)) { case P_FACTION: /* TARNE PARTEI [NICHT|NUMMER abcd] */ s = gettoken(token, sizeof(token)); if (rule_stealth_anon()) { if (!s || *s == 0) { u->flags |= UFL_ANON_FACTION; break; } else if (findparam(s, u->faction->locale) == P_NOT) { u->flags &= ~UFL_ANON_FACTION; break; } } if (rule_stealth_other()) { if (get_keyword(s, u->faction->locale) == K_NUMBER) { int nr = -1; s = gettoken(token, sizeof(token)); if (s) { nr = atoi36(s); } if (!s || *s == 0 || nr == u->faction->no) { a_removeall(&u->attribs, &at_otherfaction); break; } else { struct faction *f = findfaction(nr); if (f == NULL || !can_set_factionstealth(u, f)) { cmistake(u, ord, 66, MSG_EVENT); break; } else { set_factionstealth(u, f); break; } } } } cmistake(u, ord, 289, MSG_EVENT); break; case P_ANY: case P_NOT: /* TARNE ALLES (was nicht so alles geht?) */ u_seteffstealth(u, -1); break; default: if (skill_enabled(SK_STEALTH)) { /* hack! E3 erlaubt keine Tarnung */ stealth_race(u, s); } } } return 0; } static int top_skill(region * r, faction * f, ship * sh, skill_t sk) { int value = 0; unit *u; for (u = r->units; u; u = u->next) { if (u->ship == sh && u->faction == f) { int s = effskill(u, sk, NULL); if (value < s) value = s; } } return value; } static int try_destruction(unit * u, unit * u2, const ship * sh, int skilldiff) { const char *destruction_success_msg = "destroy_ship_0"; const char *destruction_failed_msg = "destroy_ship_1"; const char *destruction_detected_msg = "destroy_ship_2"; const char *detect_failure_msg = "destroy_ship_3"; const char *object_destroyed_msg = "destroy_ship_4"; if (skilldiff == 0) { /* tell the unit that the attempt failed: */ ADDMSG(&u->faction->msgs, msg_message(destruction_failed_msg, "ship unit", sh, u)); /* tell the enemy about the attempt: */ if (u2) { ADDMSG(&u2->faction->msgs, msg_message(detect_failure_msg, "ship", sh)); } return 0; } else if (skilldiff < 0) { /* tell the unit that the attempt was detected: */ ADDMSG(&u->faction->msgs, msg_message(destruction_detected_msg, "ship unit", sh, u)); /* tell the enemy whodunit: */ if (u2) { ADDMSG(&u2->faction->msgs, msg_message(detect_failure_msg, "ship", sh)); } return 0; } else { /* tell the unit that the attempt succeeded */ ADDMSG(&u->faction->msgs, msg_message(destruction_success_msg, "ship unit", sh, u)); if (u2) { ADDMSG(&u2->faction->msgs, msg_message(object_destroyed_msg, "ship", sh)); } } return 1; /* success */ } void sink_ship(ship * sh) { unit *u; region *r; message *sink_msg = NULL; faction *f; assert(sh && sh->region); r = sh->region; for (f = NULL, u = r->units; u; u = u->next) { /* slight optimization to avoid dereferencing u->faction each time */ if (f != u->faction) { f = u->faction; f->flags &= ~FFL_SELECT; } } for (f = NULL, u = r->units; u; u = u->next) { /* inform this faction about the sinking ship: */ if (u->ship == sh) { if (f != u->faction) { f = u->faction; if (!(f->flags & FFL_SELECT)) { f->flags |= FFL_SELECT; if (sink_msg == NULL) { sink_msg = msg_message("sink_msg", "ship region", sh, r); } add_message(&f->msgs, sink_msg); } } } else if (f != NULL) { break; } } if (sink_msg) { msg_release(sink_msg); } } int sabotage_cmd(unit * u, struct order *ord) { const char *s; param_t p; ship *sh; unit *u2; int skdiff = INT_MAX; assert(u); assert(ord); init_order(ord, NULL); s = getstrtoken(); p = findparam(s, u->faction->locale); reset_order(); switch (p) { case P_SHIP: sh = u->ship; if (!sh) { cmistake(u, u->thisorder, 144, MSG_EVENT); return 0; } u2 = ship_owner(sh); if (u2->faction != u->faction) { skdiff = effskill(u, SK_SPY, NULL) - top_skill(u->region, u2->faction, sh, SK_PERCEPTION); } if (try_destruction(u, u2, sh, skdiff)) { sink_ship(sh); /* finally, get rid of the ship */ remove_ship(&sh->region->ships, sh); } break; default: cmistake(u, u->thisorder, 9, MSG_EVENT); return 0; } return 0; }