int shark(void) { int arg; time_t now; char *p; struct lonstr loan; struct natstr *natp; struct natstr *oldie; double owed; int payment; char buf[1024]; if (!opt_LOANS) { pr("Loans are not enabled.\n"); return RET_FAIL; } p = getstarg(player->argp[1], "Transfer which loan #: ", buf); if (!p) return RET_SYN; if (*p == 0) return RET_SYN; arg = atoi(p); if (arg < 0) return RET_SYN; /* Check if it's a valid loan to shark. That means, is it a valid loan, not owed to this player, with a valid duration and it's been signed. */ if (!getloan(arg, &loan) || (loan.l_loner == player->cnum) || (loan.l_ldur == 0) || (loan.l_status != LS_SIGNED)) { pr("Invalid loan\n"); return RET_FAIL; } /* If we got here, we check to see if it's been defaulted on. */ owed = loan_owed(&loan, time(&now)); if (now <= loan.l_duedate) { pr("There has been no default on loan %d\n", arg); return RET_FAIL; } pr("That loan is worth $%.2f.\n", owed); natp = getnatp(player->cnum); payment = (int)ceil(owed * (1.0 + loan.l_irate / 100.0)); if (payment > natp->nat_money - 100.0) { pr("You do not have enough to cover that loan\n"); return RET_FAIL; } else { wu(0, loan.l_lonee, "%s bought loan #%d. You now owe him!\n", cname(player->cnum), arg); wu(0, loan.l_loner, "%s bought loan #%d out from under you for %d\n", cname(player->cnum), arg, payment); pr("You now own loan #%d. Go break some legs.\n", arg); } oldie = getnatp(loan.l_loner); oldie->nat_money += payment; player->dolcost += payment; loan.l_loner = player->cnum; putloan(arg, &loan); return RET_OK; }
/* * Is object @p of type @type visible to the player? * TODO: Fold this into interators. */ static int xdvisible(int type, void *p) { struct empobj *gp = p; struct lonstr *lp = p; struct natstr *natp; int tlev; if (!empobj_in_use(type, p)) return 0; switch (type) { case EF_SECTOR: case EF_SHIP: case EF_PLANE: case EF_LAND: case EF_NUKE: case EF_LOST: case EF_REALM: return gp->own == player->cnum || player->god; case EF_COUNTRY: return gp->own == player->cnum; case EF_NEWS: return !opt_HIDDEN || player->god; /* FIXME */ case EF_LOAN: if (lp->l_status == LS_SIGNED) return 1; return lp->l_loner == player->cnum || lp->l_lonee == player->cnum || player->god; case EF_SHIP_CHR: tlev = ((struct mchrstr *)p)->m_tech; goto tech; case EF_PLANE_CHR: tlev = ((struct plchrstr *)p)->pl_tech; goto tech; case EF_LAND_CHR: tlev = ((struct lchrstr *)p)->l_tech; tech: natp = getnatp(player->cnum); return player->god || tlev <= (int)(1.25 * natp->nat_level[NAT_TLEV]); case EF_NUKE_CHR: tlev = ((struct nchrstr *)p)->n_tech; if (drnuke_const > MIN_DRNUKE_CONST) { natp = getnatp(player->cnum); if (tlev > (int)((int)(1.25 * natp->nat_level[NAT_RLEV]) / drnuke_const)) return player->god; } goto tech; case EF_TABLE: return ((struct empfile *)p)->cadef != NULL; default: return 1; } }
void prepare_sects(int etu, struct bp *bp) { struct sctstr *sp; struct natstr *np; int n, civ_tax, uw_tax, mil_pay; memset(levels, 0, sizeof(levels)); /* Process all the fallout. */ if (opt_FALLOUT) { if (!player->simulation) { /* First, we determine which sectors to process fallout in */ for (n = 0; NULL != (sp = getsectid(n)); n++) sp->sct_updated = sp->sct_fallout != 0; /* Next, we process the fallout there */ for (n = 0; NULL != (sp = getsectid(n)); n++) if (sp->sct_updated) do_fallout(sp, etu); /* Next, we spread the fallout */ for (n = 0; NULL != (sp = getsectid(n)); n++) if (sp->sct_updated) spread_fallout(sp, etu); /* Next, we decay the fallout */ for (n = 0; NULL != (sp = getsectid(n)); n++) if (sp->sct_fallout) decay_fallout(sp, etu); } } for (n = 0; NULL != (sp = getsectid(n)); n++) { sp->sct_updated = 0; if (sp->sct_type == SCT_WATER) continue; bp_set_from_sect(bp, sp); np = getnatp(sp->sct_own); if (np->nat_stat != STAT_SANCT) { guerrilla(sp); do_plague(sp, np, etu); tax(sp, np, etu, &pops[sp->sct_own], &civ_tax, &uw_tax, &mil_pay); np->nat_money += civ_tax + uw_tax + mil_pay; if (sp->sct_type == SCT_BANK) np->nat_money += bank_income(sp, etu); } } for (n = 0; NULL != (np = getnatp(n)); n++) { np->nat_money += upd_slmilcosts(np->nat_cnum, etu); } }
/* * Send C_FLASH text to everyone. * Format text to send using printf-style @format and optional * arguments. It is assumed to be plain ASCII. * Prefix text it with a header suitable for broadcast from deity. * Initiate an output queue flush, but do not wait for it to complete. */ void pr_wall(char *format, ...) { time_t now; struct tm *tm; char buf[4096]; /* UTF-8 */ int n; struct player *p; va_list ap; time(&now); tm = localtime(&now); n = sprintf(buf, "BROADCAST from %s @ %02d:%02d: ", getnatp(0)->nat_cnam, tm->tm_hour, tm->tm_min); va_start(ap, format); (void)vsprintf(buf + n, format, ap); va_end(ap); for (p = player_next(NULL); p; p = player_next(p)) { if (p->state != PS_PLAYING) continue; pr_player(p, C_FLASH, buf); io_output(p->iop, 0); } }
int check_nat_name(char *cname, natid cnum) { struct natstr *natp; natid cn; int allblank; char *p; if (strlen(cname) >= sizeof(natp->nat_cnam)) { pr("Country name too long\n"); return 0; } allblank = 1; for (p = cname; *p != '\0'; p++) { if (iscntrl(*p)) { pr("No control characters allowed in country names!\n"); return 0; } else if (!isspace(*p)) allblank = 0; } if (allblank) { pr("Country name can't be all blank\n"); return 0; } for (cn = 0; NULL != (natp = getnatp(cn)); cn++) { if (cn != cnum && !strcmp(cname, natp->nat_cnam)) { pr("Country #%d is already called `%s'\n", cn, cname); return 0; } } return 1; }
/* * translate @x,y:int into * result params */ int sarg_range(char *str, coord *xp, coord *yp, int *dist) { coord x, y; long d; char *end; struct natstr *np; if (*str++ != '@') return 0; x = strtox(str, &str); if (x < 0 || *str++ != ',') return 0; y = strtoy(str, &str); if (y < 0 || *str++ != ':') return 0; d = strtol(str, &end, 10); if (end == str || (*end != 0 && !isspace(*end)) || d < 0) return 0; *dist = d; np = getnatp(player->cnum); *xp = xabs(np, x); *yp = yabs(np, y); return 1; }
/* * Get nation argument. * If @arg is not empty, use it * Else prompt for input using @prompt. * If no input is provided, return NULL. * If the argument identifies a country, return its getnatp() value. * Else complain and return NULL. * Caution: this function doesn't care for lack of contact. */ struct natstr * natargp(char *arg, char *prompt) { char buf[1024]; int n; struct natstr *np; arg = getstarg(arg, prompt, buf); if (!arg || !*arg) return NULL; if (isdigit(*arg)) n = atoi(arg); else { n = cnumb(arg); if (n == M_NOTUNIQUE) { pr("Country '%s' is ambiguous\n", arg); return NULL; } } np = getnatp(n); if (!np || np->nat_stat == STAT_UNUSED) { pr("Country '%s' doesn't exist.\n", arg); return NULL; } return np; }
/* * translate #1 or lx:ly,hx:hy into a result range struct * returns absolute coords */ int sarg_area(char *str, struct range *rp) { long rlm; struct natstr *np; struct realmstr realm; char *end; if (*str == '#') { /* * realm #X where (X > 0 && X < MAXNOR) * Assumes realms are in abs coordinates */ if (*++str) { rlm = strtol(str, &end, 10); if (end == str || (*end != 0 && !isspace(*end)) || rlm < 0 || MAXNOR <= rlm) return 0; } else rlm = 0; getrealm(rlm, player->cnum, &realm); rp->lx = realm.r_xl; rp->hx = realm.r_xh; rp->ly = realm.r_yl; rp->hy = realm.r_yh; } else { /* * full map specification * LX:LY,HX:HY where * ly, hy are optional. */ rp->lx = rp->hx = strtox(str, &str); if (rp->lx < 0) return 0; if (*str == ':') { rp->hx = strtox(str + 1, &str); if (rp->hx < 0) return 0; } if (*str++ != ',') return 0; rp->ly = rp->hy = strtoy(str, &str); if (rp->ly < 0) return 0; if (*str == ':') { rp->hy = strtoy(str + 1, &str); if (rp->hy < 0) return 0; } if (*str != 0 && !isspace(*str)) return 0; np = getnatp(player->cnum); rp->lx = xabs(np, rp->lx); rp->hx = xabs(np, rp->hx); rp->ly = yabs(np, rp->ly); rp->hy = yabs(np, rp->hy); } xysize_range(rp); return 1; }
/* Extended dump command */ int xdump(void) { char *p; char buf[1024]; struct xdstr xd; struct natstr *natp; int type; int meta = 0; p = getstarg(player->argp[1], "Table name, or meta? ", buf); if (p && strcmp(p, "meta") == 0) { meta = 1; p = getstarg(player->argp[2], "Table name? ", buf); } if (!p || !*p) return RET_SYN; xdinit(&xd, player->cnum, 0, 0, pr); natp = getnatp(player->cnum); type = isdigit(p[0]) ? atoi(p) : ef_byname(p); if (type < 0 || type >= EF_MAX) return RET_SYN; if (meta) return xdmeta(&xd, type); if ((EF_IS_GAME_STATE(type) || EF_IS_VIEW(type)) && !(natp->nat_stat == STAT_ACTIVE || player->god)) { pr("Access to table %s denied\n", ef_nameof(type)); return RET_FAIL; } return xditem(&xd, type, player->argp[2]); }
/* * Print coordinates @x,@y. * @format must be a printf-style format string that converts exactly * two int values. */ void prxy(char *format, coord x, coord y) { struct natstr *np; np = getnatp(player->cnum); pr(format, xrel(np, x), yrel(np, y)); }
/* * Sound the current player's bell. */ void pr_beep(void) { struct natstr *np = getnatp(player->cnum); if (np->nat_flags & NF_BEEP) pr("\07"); }
/* * Accept a loan. If the offering country has too little money, * leave him $100 left and offer the rest. Return RET_OK on * success, anything else on error. */ static int loan_accept(struct ltcomstr *ltcp) { struct lonstr *lp; struct natstr *lender; struct nstr_item nstr; struct lonstr loan; lp = <cp->u.l; if (ltcp->proposee != player->cnum) { pr("%s %d is still pending.\n", ltcp->Name, ltcp->num); return RET_OK; } if (!getloan(ltcp->num, lp)) { logerror("loan_accept: can't read loan"); pr("can't read loan; get help!\n"); return RET_FAIL; } if (lp->l_status == LS_FREE) { /* other guy retratcted already */ late(ltcp); return RET_OK; } if (lp->l_status == LS_SIGNED) { /* already signed somehow */ prev_signed(ltcp); return RET_OK; } /* check to see if a loan already exists */ snxtitem_all(&nstr, EF_LOAN); while (nxtitem(&nstr, &loan)) { if (loan.l_status == LS_SIGNED && loan.l_lonee == lp->l_loner && (loan.l_loner == lp->l_lonee)) { pr("He already owes you money - make him repay his loan!\n"); return RET_OK; } } lender = getnatp(ltcp->proposer); if (lender->nat_money < lp->l_amtdue) { /* other guy is poor */ lp->l_amtdue = lender->nat_money - 100; pr("%s no longer has the funds.\n", cname(ltcp->proposer)); if (lp->l_amtdue <= 0) return RET_FAIL; pr("You may borrow $%d at the same terms.\n", lp->l_amtdue); } lender->nat_money -= lp->l_amtdue; putnat(lender); player->dolcost -= lp->l_amtdue; lp->l_amtpaid = 0; (void)time(&lp->l_lastpay); lp->l_duedate = lp->l_ldur * SECS_PER_DAY + lp->l_lastpay; lp->l_status = LS_SIGNED; if (!putloan(ltcp->num, lp)) { pr("Problem writing lp->to disk; get help!\n"); return RET_FAIL; } accpt(ltcp); pr("You are now $%d richer (sort of).\n", lp->l_amtdue); return RET_OK; }
int orig(void) { char *p; coord x, y; char buf[1024]; int cnum; struct natstr *np; p = getstarg(player->argp[1], "New origin (sector or country) : ", buf); if (!p || !*p) return RET_SYN; if (!isalpha(*p) && strchr(p, ',')) { /* sector */ if (!sarg_xy(p, &x, &y)) { pr("Bad sector designation.\n"); return RET_SYN; } } else if (*p == '~') { /* reset */ if (!player->god) { pr("Only deities can reset their origin.\n"); return RET_FAIL; } x = y = 0; } else { /* country */ cnum = natarg(p, NULL); if (!(np = getnatp(cnum))) return RET_SYN; if (!player->god && relations_with(cnum, player->cnum) != ALLIED) { pr("Country %s is not allied with you!\n", np->nat_cnam); return RET_FAIL; } x = np->nat_xorg; y = np->nat_yorg; } pr("Origin at %s (old system) is now at 0,0 (new system).\n", xyas(x, y, player->cnum)); np = getnatp(player->cnum); np->nat_xorg = x; np->nat_yorg = y; putnat(np); return RET_OK; }
static int play_cmd(void) { struct player *other; natid cnum; struct natstr *natp; char **ap; char buf[128]; ap = player->argp; if (*++ap) { strncpy(player->userid, *ap, sizeof(player->userid) - 1); player->userid[sizeof(player->userid) - 1] = '\0'; player->authenticated = 0; } if (*++ap) { if (natbyname(*ap, &cnum) < 0) { pr_id(player, C_CMDERR, "country %s does not exist\n", *ap); return RET_FAIL; } } if (*++ap) { if (!natpass(cnum, *ap)) { pr_id(player, C_CMDERR, "password bad, logging entry\n"); logerror("%s tried country #%d with %s", praddr(player), cnum, *ap); return RET_FAIL; } player->cnum = cnum; player->authenticated = 1; } if (!may_play()) return RET_FAIL; other = getplayer(player->cnum); if (other) { natp = getnatp(player->cnum); if (natp->nat_stat != STAT_VIS) { pr_id(player, C_EXIT, "country in use by %s\n", praddr(other)); } else { pr_id(player, C_EXIT, "country in use\n"); } return RET_FAIL; } snprintf(buf, sizeof(buf), "Play#%d", player->cnum); logerror("%s logged in as country #%d", praddr(player), player->cnum); journal_login(); empth_set_name(empth_self(), buf); pr_id(player, C_INIT, "%d\n", CLIENTPROTO); empth_rwlock_rdlock(shutdown_lock); player->state = PS_PLAYING; player_main(player); journal_logout(); logerror("%s logged out, country #%d", praddr(player), player->cnum); if (!io_eof(player->iop) && !io_error(player->iop)) io_set_eof(player->iop); return RET_OK; }
char * prnatid(natid cnum) { struct natstr *np = getnatp(cnum); if (CANT_HAPPEN(!np)) return prbuf("%d (#%d)", cnum, cnum); return prnat(np); }
void setcont(natid us, natid them, int contact) { struct natstr *np = getnatp(us); if (CANT_HAPPEN(!np)) return; putcontact(np, them, contact); putnat(np); }
void setrej(natid us, natid them, int how, int what) { struct natstr *np = getnatp(us); if (CANT_HAPPEN(!np)) return; putreject(np, them, how, what); putnat(np); }
/* * Initialize @xd. * If @cnum is NATID_BAD, this is an empdump export rather than an * xdump command. * Translate dump for country @cnum, except when @cnum is NATID_BAD. * Ignore CA_DUMP_ONLY selectors when @cnum is NATID_BAD. * If @human, dump in human-readable format. * If @sloppy, try to cope with invalid data (may result in invalid * dump). * Dump is to be delivered through callback @pr. * Return @xd. */ struct xdstr * xdinit(struct xdstr *xd, natid cnum, int human, int sloppy, void (*pr)(char *fmt, ...)) { xd->cnum = cnum; xd->divine = cnum == NATID_BAD || getnatp(cnum)->nat_stat == STAT_GOD; xd->human = human; xd->sloppy = sloppy; xd->pr = pr; return xd; }
double tfact(natid cn, double mult) { double tlev; struct natstr *np; np = getnatp(cn); tlev = np->nat_level[NAT_TLEV]; tlev = (50.0 + tlev) / (200.0 + tlev); return mult * tlev; }
void enforce_minimum_session_time(void) { struct natstr *natp = getnatp(player->cnum); time_t dt = natp->nat_last_logout - natp->nat_last_login; if (dt > seconds_since_midnight(natp->nat_last_logout)) dt = seconds_since_midnight(natp->nat_last_logout); if (dt < 15) natp->nat_timeused += 15 - dt; putnat(natp); }
void update_timeused_login(time_t now) { struct natstr *natp = getnatp(player->cnum); time_t midnight_secs = seconds_since_midnight(now); if (now - natp->nat_last_logout > midnight_secs) { natp->nat_timeused = 0; putnat(natp); } player->lasttime = now; }
int natpass(natid cn, char *pass) { struct natstr *np; np = getnatp(cn); if (np->nat_stat == STAT_VIS) return 1; if (strcmp(np->nat_pnam, pass) == 0) return 1; return 0; }
void update_timeused(time_t now) { struct natstr *natp = getnatp(player->cnum); time_t midnight_secs = seconds_since_midnight(now); time_t dt = now - player->lasttime; if (dt > midnight_secs) natp->nat_timeused = midnight_secs; else natp->nat_timeused += dt; player->lasttime = now; putnat(natp); }
int natbyname(char *name, natid *result) { struct natstr *np; int i; for (i = 0; NULL != (np = getnatp(i)); i++) { if (np->nat_stat != STAT_UNUSED && !strcmp(np->nat_cnam, name)) { *result = i; return 0; } } *result = NATID_BAD; return -1; }
/* * Get nation argument. * If @arg is not empty, use it. * Else prompt for input using @prompt. * If no input is provided, return -1. * If the argument identifies a country, return its number. getnatp() * can be assumed to succeed for this number. * Else complain and return -1. * If HIDDEN is enabled, countries not contacted are not eligible * unless the player is a deity. */ int natarg(char *arg, char *prompt) { struct natstr *np = natargp(arg, prompt); if (!np) return -1; if (opt_HIDDEN) { if (!player->god && !getcontact(getnatp(player->cnum), np->nat_cnum)) { if (np->nat_stat != STAT_GOD) { pr("Country '%s' has not been contacted.\n", arg); return -1; } } } return np->nat_cnum; }
int real(void) { struct realmstr realm; struct natstr *natp; int curr; int lastr; struct range abs; char *realmp = player->argp[1]; natp = getnatp(player->cnum); if (!realmp) { curr = 0; lastr = MAXNOR - 1; } else { if (*realmp == '#') ++realmp; if (*realmp && !isdigit(*realmp)) return RET_SYN; curr = lastr = atoi(realmp); if (curr < 0 || curr >= MAXNOR) { pr("Realm number must be in the range 0:%d\n", MAXNOR - 1); return RET_SYN; } } if (!player->argp[2]) { while (curr <= lastr) { list_realm(curr, natp); curr++; } } else { if (sarg_type(player->argp[2]) != NS_AREA) return RET_SYN; abs.width = 0; abs.height = 0; if (!sarg_area(player->argp[2], &abs)) return RET_SYN; getrealm(curr, natp->nat_cnum, &realm); realm.r_xl = abs.lx; realm.r_xh = abs.hx; realm.r_yl = abs.ly; realm.r_yh = abs.hy; putrealm(&realm); list_realm(curr, natp); } return RET_OK; }
/* * setup the nstr_sect structure for sector selection. * can select on either NS_ALL, NS_AREA, or NS_DIST * iterate thru the "condarg" string looking * for arguments to compile into the nstr. * Using this function for anything but command arguments is usually * incorrect, because it respects conditionals. Use the snxtsct_FOO() * instead. */ int snxtsct(struct nstr_sect *np, char *str) { struct range range; struct natstr *natp; coord cx, cy; int dist; char buf[1024]; if (!str || !*str) { if (!(str = getstring("(sects)? ", buf))) return 0; } else make_stale_if_command_arg(str); switch (sarg_type(str)) { case NS_AREA: if (!sarg_area(str, &range)) return 0; snxtsct_area(np, &range); break; case NS_DIST: if (!sarg_range(str, &cx, &cy, &dist)) return 0; snxtsct_dist(np, cx, cy, dist); break; case NS_ALL: /* * Can't use snxtsct_all(), as it would disclose the real * origin. Use a world-sized area instead. */ natp = getnatp(player->cnum); range.lx = xabs(natp, -WORLD_X / 2); range.ly = yabs(natp, -WORLD_Y / 2); range.hx = xnorm(range.lx + WORLD_X - 1); range.hy = ynorm(range.ly + WORLD_Y - 1); xysize_range(&range); snxtsct_area(np, &range); break; default: return 0; } return snxtsct_use_condarg(np); }
int sarg_xy(char *str, coord *xp, coord *yp) { coord x, y; struct natstr *np; x = strtox(str, &str); if (x < 0 || *str++ != ',') return 0; y = strtoy(str, &str); if (y < 0 || (*str != 0 && !isspace(*str))) return 0; if ((x ^ y) & 1) return 0; np = getnatp(player->cnum); *xp = xabs(np, x); *yp = yabs(np, y); return 1; }
int prod_plane(int etus, int natnum, struct bp *bp, int buildem) /* Build = 1, maintain =0 */ { struct plnstr *pp; struct natstr *np; int n, k = 0; int start_money; for (n = 0; NULL != (pp = getplanep(n)); n++) { if (pp->pln_own == 0) continue; if (pp->pln_own != natnum) continue; if (pp->pln_effic < PLANE_MINEFF) { makelost(EF_PLANE, pp->pln_own, pp->pln_uid, pp->pln_x, pp->pln_y); pp->pln_own = 0; continue; } if (pln_is_in_orbit(pp)) { if (!player->simulation && buildem == 0 && !(pp->pln_flags & PLN_SYNCHRONOUS)) move_sat(pp); continue; } np = getnatp(pp->pln_own); start_money = np->nat_money; upd_plane(pp, etus, np, bp, buildem); air_money[pp->pln_own] += np->nat_money - start_money; if (buildem == 0 || np->nat_money != start_money) k++; if (player->simulation) np->nat_money = start_money; } return k; }
static int may_play(void) { struct natstr *np; if (player->cnum == NATID_BAD || !player->authenticated) { pr_id(player, C_CMDERR, "need country and password\n"); return 0; } /* TODO strstr() cheesy, compare IP against IP/BITS ... */ np = getnatp(player->cnum); if (np->nat_stat == STAT_GOD && *privip && !strstr(privip, player->hostaddr)) { logerror("Deity login from untrusted host attempted by %s", praddr(player)); logerror("To allow this, add %s to econfig key privip", player->hostaddr); pr_id(player, C_EXIT, "Deity login not allowed from this IP!" " See log for help on how to allow it.\n"); return 0; } return 1; }