// TODO: Shunt redundant drawing code elsewhere std::vector<point> game::target(int &x, int &y, int lowx, int lowy, int hix, int hiy, std::vector <monster> t, int &target, item *relevent) { std::vector<point> ret; int tarx, tary, junk, tart; int range=(hix-u.posx); // First, decide on a target among the monsters, if there are any in range if (!t.empty()) { // Check for previous target if (target == -1) { // If no previous target, target the closest there is double closest = -1; double dist; for (int i = 0; i < t.size(); i++) { dist = rl_dist(t[i].posx(), t[i].posy(), u.posx, u.posy); if (closest < 0 || dist < closest) { closest = dist; target = i; } } } x = t[target].posx(); y = t[target].posy(); } else target = -1; // No monsters in range, don't use target, reset to -1 bool sideStyle = use_narrow_sidebar(); int height = 13; int width = getmaxx(w_messages); int top = sideStyle ? getbegy(w_messages) : (getbegy(w_minimap) + getmaxy(w_minimap)); int left = getbegx(w_messages); WINDOW* w_target = newwin(height, width, top, left); draw_border(w_target); mvwprintz(w_target, 0, 2, c_white, "< "); if (!relevent) { // currently targetting vehicle to refill with fuel wprintz(w_target, c_red, _("Select a vehicle")); } else { if (relevent == &u.weapon && relevent->is_gun()) { wprintz(w_target, c_red, _("Firing %s (%d)"), // - %s (%d)", u.weapon.tname().c_str(),// u.weapon.curammo->name.c_str(), u.weapon.charges); } else { wprintz(w_target, c_red, _("Throwing %s"), relevent->tname().c_str()); } } wprintz(w_target, c_white, " >"); /* Annoying clutter @ 2 3 4. */ int text_y = getmaxy(w_target) - 4; if (is_mouse_enabled()) { --text_y; } mvwprintz(w_target, text_y++, 1, c_white, _("Move cursor to target with directional keys")); if (relevent) { mvwprintz(w_target, text_y++, 1, c_white, _("'<' '>' Cycle targets; 'f' or '.' to fire")); mvwprintz(w_target, text_y++, 1, c_white, _("'0' target self; '*' toggle snap-to-target")); } if (is_mouse_enabled()) { mvwprintz(w_target, text_y++, 1, c_white, _("Mouse: LMB: Target, Wheel: Cycle, RMB: Fire")); } wrefresh(w_target); bool snap_to_target = OPTIONS["SNAP_TO_TARGET"]; do { if (m.sees(u.posx, u.posy, x, y, -1, tart)) ret = line_to(u.posx, u.posy, x, y, tart); else ret = line_to(u.posx, u.posy, x, y, 0); if(trigdist && trig_dist(u.posx,u.posy, x,y) > range) { bool cont=true; int cx=x; int cy=y; for (int i = 0; i < ret.size() && cont; i++) { if(trig_dist(u.posx,u.posy, ret[i].x, ret[i].y) > range) { ret.resize(i); cont=false; } else { cx=0+ret[i].x; cy=0+ret[i].y; } } x=cx;y=cy; } point center; if (snap_to_target) center = point(x, y); else center = point(u.posx + u.view_offset_x, u.posy + u.view_offset_y); // Clear the target window. for (int i = 1; i < getmaxy(w_target) - 5; i++) { for (int j = 1; j < getmaxx(w_target) - 2; j++) mvwputch(w_target, i, j, c_white, ' '); } /* Start drawing w_terrain things -- possibly move out to centralized draw_terrain_window function as they all should be roughly similar*/ m.build_map_cache(); // part of the SDLTILES drawing code m.draw(w_terrain, center); // embedded in SDL drawing code // Draw the Monsters for (int i = 0; i < num_zombies(); i++) { if (u_see(&(zombie(i)))) { zombie(i).draw(w_terrain, center.x, center.y, false); } } // Draw the NPCs for (int i = 0; i < active_npc.size(); i++) { if (u_see(active_npc[i]->posx, active_npc[i]->posy)) active_npc[i]->draw(w_terrain, center.x, center.y, false); } if (x != u.posx || y != u.posy) { // Draw the player int atx = POSX + u.posx - center.x, aty = POSY + u.posy - center.y; if (atx >= 0 && atx < TERRAIN_WINDOW_WIDTH && aty >= 0 && aty < TERRAIN_WINDOW_HEIGHT) mvwputch(w_terrain, aty, atx, u.color(), '@'); // Only draw a highlighted trajectory if we can see the endpoint. // Provides feedback to the player, and avoids leaking information about tiles they can't see. draw_line(x, y, center, ret); /* if (u_see( x, y)) { for (int i = 0; i < ret.size(); i++) { int mondex = mon_at(ret[i].x, ret[i].y), npcdex = npc_at(ret[i].x, ret[i].y); // NPCs and monsters get drawn with inverted colors if (mondex != -1 && u_see(&(zombie(mondex)))) zombie(mondex).draw(w_terrain, center.x, center.y, true); else if (npcdex != -1) active_npc[npcdex]->draw(w_terrain, center.x, center.y, true); else m.drawsq(w_terrain, u, ret[i].x, ret[i].y, true,true,center.x, center.y); } } //*/ // Print to target window if (!relevent) { // currently targetting vehicle to refill with fuel vehicle *veh = m.veh_at(x, y); if (veh) { mvwprintw(w_target, 1, 1, _("There is a %s"), veh->name.c_str()); } } else if (relevent == &u.weapon && relevent->is_gun()) { // firing a gun mvwprintw(w_target, 1, 1, _("Range: %d"), rl_dist(u.posx, u.posy, x, y)); // get the current weapon mode or mods std::string mode = ""; if (u.weapon.mode == "MODE_BURST") { mode = _("Burst"); } else { item* gunmod = u.weapon.active_gunmod(); if (gunmod != NULL) { mode = gunmod->type->name; } } if (mode != "") { mvwprintw(w_target, 1, 14, _("Firing mode: %s"), mode.c_str()); } } else { // throwing something mvwprintw(w_target, 1, 1, _("Range: %d"), rl_dist(u.posx, u.posy, x, y)); } const int zid = mon_at(x, y); if (zid == -1) { if (snap_to_target) mvwputch(w_terrain, POSY, POSX, c_red, '*'); else mvwputch(w_terrain, POSY + y - center.y, POSX + x - center.x, c_red, '*'); } else { if (u_see(&(zombie(zid)))) { zombie(zid).print_info(w_target,2); } } } wrefresh(w_target); wrefresh(w_terrain); wrefresh(w_status); refresh(); input_context ctxt("TARGET"); // "ANY_INPUT" should be added before any real help strings // Or strings will be writen on window border. ctxt.register_action("ANY_INPUT"); ctxt.register_directions(); ctxt.register_action("COORDINATE"); ctxt.register_action("SELECT"); ctxt.register_action("FIRE"); ctxt.register_action("NEXT_TARGET"); ctxt.register_action("PREV_TARGET"); ctxt.register_action("WAIT"); ctxt.register_action("CENTER"); ctxt.register_action("TOGGLE_SNAP_TO_TARGET"); ctxt.register_action("HELP_KEYBINDINGS"); ctxt.register_action("QUIT"); const std::string& action = ctxt.handle_input(); tarx = 0; tary = 0; // Our coordinates will either be determined by coordinate input(mouse), // by a direction key, or by the previous value. if (action == "SELECT" && ctxt.get_coordinates(g->w_terrain, tarx, tary)) { if (!OPTIONS["USE_TILES"] && snap_to_target) { // Snap to target doesn't currently work with tiles. tarx += x - u.posx; tary += y - u.posy; } tarx -= x; tary -= y; } else { ctxt.get_direction(tarx, tary, action); if(tarx == -2) { tarx = 0; tary = 0; } } /* More drawing to terrain */ if (tarx != 0 || tary != 0) { int mondex = mon_at(x, y), npcdex = npc_at(x, y); if (mondex != -1 && u_see(&(zombie(mondex)))) zombie(mondex).draw(w_terrain, center.x, center.y, false); else if (npcdex != -1) active_npc[npcdex]->draw(w_terrain, center.x, center.y, false); else if (m.sees(u.posx, u.posy, x, y, -1, junk)) m.drawsq(w_terrain, u, x, y, false, true, center.x, center.y); else mvwputch(w_terrain, POSY, POSX, c_black, 'X'); x += tarx; y += tary; if (x < lowx) x = lowx; else if (x > hix) x = hix; if (y < lowy) y = lowy; else if (y > hiy) y = hiy; } else if ((action == "PREV_TARGET") && (target != -1)) { target--; if (target == -1) target = t.size() - 1; x = t[target].posx(); y = t[target].posy(); } else if ((action == "NEXT_TARGET") && (target != -1)) { target++; if (target == t.size()) target = 0; x = t[target].posx(); y = t[target].posy(); } else if (action == "WAIT" || action == "FIRE") { for (int i = 0; i < t.size(); i++) { if (t[i].posx() == x && t[i].posy() == y) target = i; } if (u.posx == x && u.posy == y) ret.clear(); break; } else if (action == "CENTER") { x = u.posx; y = u.posy; ret.clear(); } else if (action == "TOGGLE_SNAP_TO_TARGET") snap_to_target = !snap_to_target; else if (action == "QUIT") { // return empty vector (cancel) ret.clear(); break; } } while (true); return ret; }
/* * Save to opened character.sav */ void game::serialize(std::ofstream & fout) { /* * Format version 12: Fully json, save the header. Weather and memorial exist elsewhere. * To prevent (or encourage) confusion, there is no version 8. (cata 0.8 uses v7) */ // Header fout << "# version " << savegame_version << std::endl; std::map<std::string, picojson::value> data; // basic game state information. data["turn"] = pv( (int)turn ); data["last_target"] = pv( (int)last_target ); data["run_mode"] = pv( (int)run_mode ); data["mostseen"] = pv( mostseen ); data["nextinv"] = pv( (int)nextinv ); data["next_npc_id"] = pv( next_npc_id ); data["next_faction_id"] = pv( next_faction_id ); data["next_mission_id"] = pv( next_mission_id ); data["nextspawn"] = pv( (int)nextspawn ); // current map coordinates data["levx"] = pv( levx ); data["levy"] = pv( levy ); data["levz"] = pv( levz ); data["om_x"] = pv( cur_om->pos().x ); data["om_y"] = pv( cur_om->pos().y ); // Next, the scent map. std::stringstream rle_out; int rle_lastval = -1; int rle_count = 0; for (int i = 0; i < SEEX * MAPSIZE; i++) { for (int j = 0; j < SEEY * MAPSIZE; j++) { int val = grscent[i][j]; if (val == rle_lastval) { rle_count++; } else { if ( rle_count ) { rle_out << rle_count << " "; } rle_out << val << " "; rle_lastval = val; rle_count = 1; } } } rle_out << rle_count; data["grscent"] = pv ( rle_out.str() ); // Then each monster std::vector<picojson::value> amdata; for (int i = 0; i < num_zombies(); i++) { amdata.push_back( _active_monsters[i].json_save(true) ); } data["active_monsters"] = pv( amdata ); // save killcounts. std::map<std::string, picojson::value> killmap; for (std::map<std::string, int>::iterator kill = kills.begin(); kill != kills.end(); ++kill){ killmap[kill->first] = pv(kill->second); } data["kills"] = pv( killmap ); data["player"] = pv( u.json_save(true) ); fout << pv(data).serialize() << std::endl; //////// }
/* * Save to opened character.sav */ void game::serialize(std::ofstream & fout) { /* * Format version 9: Hybrid format. Ordered, line by line mix of json chunks, and stringstream bits that don't * really make sense as json. New data can be added to the basic game state json, or tacked onto the end as a * new line. * To prevent (or encourage) confusion, there is no version 8. (cata 0.8 uses v7) */ // Header fout << "# version " << savegame_version << std::endl; std::map<std::string, picojson::value> data; // basic game state information. data["turn"] = pv( (int)turn ); data["last_target"] = pv( (int)last_target ); data["run_mode"] = pv( (int)run_mode ); data["mostseen"] = pv( mostseen ); data["nextinv"] = pv( (int)nextinv ); data["next_npc_id"] = pv( next_npc_id ); data["next_faction_id"] = pv( next_faction_id ); data["next_mission_id"] = pv( next_mission_id ); data["nextspawn"] = pv( (int)nextspawn ); // current map coordinates data["levx"] = pv( levx ); data["levy"] = pv( levy ); data["levz"] = pv( levz ); data["om_x"] = pv( cur_om->pos().x ); data["om_y"] = pv( cur_om->pos().y ); fout << pv(data).serialize(); fout << std::endl; // Weather. todo: move elsewhere fout << save_weather(); fout << std::endl; // Next, the scent map. for (int i = 0; i < SEEX * MAPSIZE; i++) { for (int j = 0; j < SEEY * MAPSIZE; j++) { fout << grscent[i][j] << " "; } } // Now save all monsters. First the amount fout << std::endl << num_zombies() << std::endl; // Then each monster + inv in a 1 line json string for (int i = 0; i < num_zombies(); i++) { fout << _active_monsters[i].save_info() << std::endl; } // save killcounts. std::map<std::string, picojson::value> killmap; for (int i = 0; i < num_monsters; i++) { if ( kills[i] > 0 ) { killmap[ monster_names[ i ] ] = pv ( kills[i] ); } } fout << pv( killmap ).serialize() << std::endl; // And finally the player. // u.save_info dumps player + contents in a single json line, followed by memorial log // one entry per line starting with '|' fout << u.save_info() << std::endl; fout << std::endl; //////// }