/** * Sells all vehicles in a depot * @param tile Tile of the depot where the depot is * @param flags type of operation * @param p1 Vehicle type * @param p2 unused * @param text unused * @return the cost of this operation or an error */ CommandCost CmdDepotSellAllVehicles(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { VehicleList list; CommandCost cost(EXPENSES_NEW_VEHICLES); VehicleType vehicle_type = Extract<VehicleType, 0, 3>(p1); if (!IsCompanyBuildableVehicleType(vehicle_type)) return CMD_ERROR; uint sell_command = GetCmdSellVeh(vehicle_type); /* Get the list of vehicles in the depot */ BuildDepotVehicleList(vehicle_type, tile, &list, &list); CommandCost last_error = CMD_ERROR; bool had_success = false; for (uint i = 0; i < list.Length(); i++) { CommandCost ret = DoCommand(tile, list[i]->index | (1 << 20), 0, flags, sell_command); if (ret.Succeeded()) { cost.AddCost(ret); had_success = true; } else { last_error = ret; } } return had_success ? cost : last_error; }
/** * Starts or stops a lot of vehicles * @param tile Tile of the depot where the vehicles are started/stopped (only used for depots) * @param flags type of operation * @param p1 bitmask * - bit 0 set = start vehicles, unset = stop vehicles * - bit 1 if set, then it's a vehicle list window, not a depot and Tile is ignored in this case * @param p2 packed VehicleListIdentifier * @param text unused * @return the cost of this operation or an error */ CommandCost CmdMassStartStopVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { VehicleList list; bool do_start = HasBit(p1, 0); bool vehicle_list_window = HasBit(p1, 1); VehicleListIdentifier vli; if (!vli.UnpackIfValid(p2)) return CMD_ERROR; if (!IsCompanyBuildableVehicleType(vli.vtype)) return CMD_ERROR; if (vehicle_list_window) { if (!GenerateVehicleSortList(&list, vli)) return CMD_ERROR; } else { /* Get the list of vehicles in the depot */ BuildDepotVehicleList(vli.vtype, tile, &list, NULL); } for (uint i = 0; i < list.Length(); i++) { const Vehicle *v = list[i]; if (!!(v->vehstatus & VS_STOPPED) != do_start) continue; if (!vehicle_list_window && !v->IsChainInDepot()) continue; /* Just try and don't care if some vehicle's can't be stopped. */ DoCommand(tile, v->index, 0, flags, CMD_START_STOP_VEHICLE); } return CommandCost(); }
uint DeparturesWindow<Twaypoint>::GetMinWidth() const { uint result = 0; /* Time */ result = cached_date_width; /* Vehicle type icon */ result += _settings_client.gui.departure_show_vehicle_type ? (GetStringBoundingBox(STR_DEPARTURES_TYPE_PLANE)).width : 0; /* Status */ result += cached_status_width; /* Find the maximum company name width. */ int toc_width = 0; /* Find the maximum company name width. */ int group_width = 0; /* Find the maximum vehicle name width. */ int veh_width = 0; if (_settings_client.gui.departure_show_vehicle || _settings_client.gui.departure_show_company || _settings_client.gui.departure_show_group) { for (uint i = 0; i < 4; ++i) { VehicleList vehicles; /* MAX_COMPANIES is probably the wrong thing to put here, but it works. GenerateVehicleSortList doesn't check the company when the type of list is VL_STATION_LIST (r20801). */ if (!GenerateVehicleSortList(&vehicles, VehicleListIdentifier(VL_STATION_LIST, (VehicleType)(VEH_TRAIN + i), MAX_COMPANIES, station).Pack())) { /* Something went wrong: panic! */ continue; } for (const Vehicle **v = vehicles.Begin(); v != vehicles.End(); v++) { SetDParam(0, (uint64)((*v)->index)); int width = (GetStringBoundingBox(STR_DEPARTURES_VEH)).width; if (_settings_client.gui.departure_show_vehicle && width > veh_width) veh_width = width; if ((*v)->group_id != INVALID_GROUP && (*v)->group_id != DEFAULT_GROUP) { SetDParam(0, (uint64)((*v)->group_id)); width = (GetStringBoundingBox(STR_DEPARTURES_GROUP)).width; if (_settings_client.gui.departure_show_group && width > group_width) group_width = width; } SetDParam(0, (uint64)((*v)->owner)); width = (GetStringBoundingBox(STR_DEPARTURES_TOC)).width; if (_settings_client.gui.departure_show_company && width > toc_width) toc_width = width; } } } result += toc_width + veh_width + group_width; return result + 140; }
/** Autoreplace all vehicles in the depot * Note: this command can make incorrect cost estimations * Luckily the final price can only drop, not increase. This is due to the fact that * estimation can't predict wagon removal so it presumes worst case which is no income from selling wagons. * @param tile Tile of the depot where the vehicles are * @param flags type of operation * @param p1 Type of vehicle * @param p2 If bit 0 is set, then either replace all or nothing (instead of replacing until money runs out) * @param text unused * @return the cost of this operation or an error */ CommandCost CmdDepotMassAutoReplace(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { VehicleList list; CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES); VehicleType vehicle_type = (VehicleType)GB(p1, 0, 8); bool all_or_nothing = HasBit(p2, 0); if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR; /* Get the list of vehicles in the depot */ BuildDepotVehicleList(vehicle_type, tile, &list, &list, true); bool did_something = false; for (uint i = 0; i < list.Length(); i++) { const Vehicle *v = list[i]; /* Ensure that the vehicle completely in the depot */ if (!v->IsInDepot()) continue; CommandCost ret = DoCommand(0, v->index, 0, flags, CMD_AUTOREPLACE_VEHICLE); if (CmdSucceeded(ret)) { did_something = true; cost.AddCost(ret); } else { if (ret.GetErrorMessage() != STR_ERROR_AUTOREPLACE_NOTHING_TO_DO && all_or_nothing) { /* We failed to replace a vehicle even though we set all or nothing. * We should never reach this if DC_EXEC is set since then it should * have failed the estimation guess. */ assert(!(flags & DC_EXEC)); /* Now we will have to return an error. */ return CMD_ERROR; } } } if (!did_something) { /* Either we didn't replace anything or something went wrong. * Either way we want to return an error and not execute this command. */ cost = CMD_ERROR; } return cost; }
/** Sells all vehicles in a depot * @param tile Tile of the depot where the depot is * @param flags type of operation * @param p1 Vehicle type * @param p2 unused * @param text unused * @return the cost of this operation or an error */ CommandCost CmdDepotSellAllVehicles(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { VehicleList list; CommandCost cost(EXPENSES_NEW_VEHICLES); VehicleType vehicle_type = (VehicleType)GB(p1, 0, 8); uint sell_command = GetCmdSellVeh(vehicle_type); /* Get the list of vehicles in the depot */ BuildDepotVehicleList(vehicle_type, tile, &list, &list); for (uint i = 0; i < list.Length(); i++) { CommandCost ret = DoCommand(tile, list[i]->index, 1, flags, sell_command); if (CmdSucceeded(ret)) cost.AddCost(ret); } if (cost.GetCost() == 0) return CMD_ERROR; // no vehicles to sell return cost; }
/** Starts or stops a lot of vehicles * @param tile Tile of the depot where the vehicles are started/stopped (only used for depots) * @param flags type of operation * @param p1 Station/Order/Depot ID (only used for vehicle list windows) * @param p2 bitmask * - bit 0-4 Vehicle type * - bit 5 false = start vehicles, true = stop vehicles * - bit 6 if set, then it's a vehicle list window, not a depot and Tile is ignored in this case * - bit 8-11 Vehicle List Window type (ignored unless bit 1 is set) * @param text unused * @return the cost of this operation or an error */ CommandCost CmdMassStartStopVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { VehicleList list; CommandCost return_value = CMD_ERROR; VehicleType vehicle_type = (VehicleType)GB(p2, 0, 5); bool start_stop = HasBit(p2, 5); bool vehicle_list_window = HasBit(p2, 6); if (vehicle_list_window) { uint32 id = p1; uint16 window_type = p2 & VLW_MASK; GenerateVehicleSortList(&list, vehicle_type, _current_company, id, window_type); } else { /* Get the list of vehicles in the depot */ BuildDepotVehicleList(vehicle_type, tile, &list, NULL); } for (uint i = 0; i < list.Length(); i++) { const Vehicle *v = list[i]; if (!!(v->vehstatus & VS_STOPPED) != start_stop) continue; if (!vehicle_list_window) { if (vehicle_type == VEH_TRAIN) { if (!Train::From(v)->IsInDepot()) continue; } else { if (!(v->vehstatus & VS_HIDDEN)) continue; } } CommandCost ret = DoCommand(tile, v->index, 0, flags, CMD_START_STOP_VEHICLE); if (CmdSucceeded(ret)) { return_value = CommandCost(); /* We know that the command is valid for at least one vehicle. * If we haven't set DC_EXEC, then there is no point in continueing because it will be valid */ if (!(flags & DC_EXEC)) break; } } return return_value; }
/** * Send all vehicles of type to depots * @param type type of vehicle * @param flags the flags used for DoCommand() * @param service should the vehicles only get service in the depots * @param owner owner of the vehicles to send * @param vlw_flag tells what kind of list requested the goto depot * @param id general purpose id whoms meaning is given by @c vlw_flag; e.g. StationID for station lists * @return 0 for success and CMD_ERROR if no vehicle is able to go to depot */ CommandCost SendAllVehiclesToDepot(VehicleType type, DoCommandFlag flags, bool service, Owner owner, uint16 vlw_flag, uint32 id) { VehicleList list; GenerateVehicleSortList(&list, type, owner, id, vlw_flag); /* Send all the vehicles to a depot */ for (uint i = 0; i < list.Length(); i++) { const Vehicle *v = list[i]; CommandCost ret = DoCommand(v->tile, v->index, (service ? 1 : 0) | DEPOT_DONT_CANCEL, flags, GetCmdSendToDepot(type)); /* Return 0 if DC_EXEC is not set this is a valid goto depot command) * In this case we know that at least one vehicle can be sent to a depot * and we will issue the command. We can now safely quit the loop, knowing * it will succeed at least once. With DC_EXEC we really need to send them to the depot */ if (CmdSucceeded(ret) && !(flags & DC_EXEC)) { return CommandCost(); } } return (flags & DC_EXEC) ? CommandCost() : CMD_ERROR; }
/** * Autoreplace all vehicles in the depot * @param tile Tile of the depot where the vehicles are * @param flags type of operation * @param p1 Type of vehicle * @param p2 unused * @param text unused * @return the cost of this operation or an error */ CommandCost CmdDepotMassAutoReplace(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { VehicleList list; CommandCost cost = CommandCost(EXPENSES_NEW_VEHICLES); VehicleType vehicle_type = Extract<VehicleType, 0, 3>(p1); if (!IsCompanyBuildableVehicleType(vehicle_type)) return CMD_ERROR; if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR; /* Get the list of vehicles in the depot */ BuildDepotVehicleList(vehicle_type, tile, &list, &list, true); for (uint i = 0; i < list.Length(); i++) { const Vehicle *v = list[i]; /* Ensure that the vehicle completely in the depot */ if (!v->IsChainInDepot()) continue; CommandCost ret = DoCommand(0, v->index, 0, flags, CMD_AUTOREPLACE_VEHICLE); if (ret.Succeeded()) cost.AddCost(ret); } return cost; }
void DeparturesWindow<Twaypoint>::DrawDeparturesListItems(const Rect &r) const { int left = r.left + WD_MATRIX_LEFT; int right = r.right - WD_MATRIX_RIGHT; bool rtl = _current_text_dir == TD_RTL; bool ltr = !rtl; int text_offset = WD_FRAMERECT_RIGHT; int text_left = left + (rtl ? 0 : text_offset); int text_right = right - (rtl ? text_offset : 0); int y = r.top + 1; uint max_departures = min(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), this->departures->Length() + this->arrivals->Length()); if (max_departures > _settings_client.gui.max_departures) { max_departures = _settings_client.gui.max_departures; } byte small_font_size = _settings_client.gui.departure_larger_font ? FONT_HEIGHT_NORMAL : FONT_HEIGHT_SMALL; /* Draw the black background. */ GfxFillRect(r.left + 1, r.top, r.right - 1, r.bottom, PC_BLACK); /* Nothing selected? Then display the information text. */ bool none_selected[2] = {true, true}; for (uint i = 0; i < 4; ++i) { if (this->show_types[i]) { none_selected[0] = false; break; } } for (uint i = 0; i < 2; ++i) { if (this->departure_types[i]) { none_selected[1] = false; break; } } if (none_selected[0] || none_selected[1]) { DrawString(text_left, text_right, y + 1, STR_DEPARTURES_NONE_SELECTED); return; } /* No scheduled departures? Then display the information text. */ if (max_departures == 0) { DrawString(text_left, text_right, y + 1, STR_DEPARTURES_EMPTY); return; } /* Find the maximum possible width of the departure time and "Expt <time>" fields. */ int time_width = cached_date_width; if (!_settings_client.gui.departure_show_both) { time_width += (departure_types[0] && departure_types[1] ? cached_date_arrow_width : 0); } /* Vehicle type icon */ int type_width = _settings_client.gui.departure_show_vehicle_type ? (GetStringBoundingBox(STR_DEPARTURES_TYPE_PLANE)).width : 0; /* Find the maximum width of the status field */ int status_width = cached_status_width; /* Find the width of the "Calling at:" field. */ int calling_at_width = (GetStringBoundingBox(_settings_client.gui.departure_larger_font ? STR_DEPARTURES_CALLING_AT_LARGE : STR_DEPARTURES_CALLING_AT)).width; /* Find the maximum company name width. */ int toc_width = 0; /* Find the maximum group name width. */ int group_width = 0; /* Find the maximum vehicle name width. */ int veh_width = 0; if (_settings_client.gui.departure_show_vehicle || _settings_client.gui.departure_show_company || _settings_client.gui.departure_show_group) { for (uint i = 0; i < 4; ++i) { VehicleList vehicles; /* MAX_COMPANIES is probably the wrong thing to put here, but it works. GenerateVehicleSortList doesn't check the company when the type of list is VL_STATION_LIST (r20801). */ if (!GenerateVehicleSortList(&vehicles, VehicleListIdentifier(VL_STATION_LIST, (VehicleType)(VEH_TRAIN + i), MAX_COMPANIES, station).Pack())) { /* Something went wrong: panic! */ continue; } for (const Vehicle **v = vehicles.Begin(); v != vehicles.End(); v++) { SetDParam(0, (uint64)((*v)->index)); int width = (GetStringBoundingBox(STR_DEPARTURES_VEH)).width; if (_settings_client.gui.departure_show_vehicle && width > veh_width) veh_width = width; if ((*v)->group_id != INVALID_GROUP && (*v)->group_id != DEFAULT_GROUP) { SetDParam(0, (uint64)((*v)->group_id)); width = (GetStringBoundingBox(STR_DEPARTURES_GROUP)).width; if (_settings_client.gui.departure_show_group && width > group_width) group_width = width; } SetDParam(0, (uint64)((*v)->owner)); width = (GetStringBoundingBox(STR_DEPARTURES_TOC)).width; if (_settings_client.gui.departure_show_company && width > toc_width) toc_width = width; } } } uint departure = 0; uint arrival = 0; /* Draw each departure. */ for (uint i = 0; i < max_departures; ++i) { const Departure *d; if (arrival == this->arrivals->Length()) { d = (*(this->departures))[departure++]; } else if (departure == this->departures->Length()) { d = (*(this->arrivals))[arrival++]; } else { d = (*(this->departures))[departure]; const Departure *a = (*(this->arrivals))[arrival]; if (a->scheduled_date < d->scheduled_date) { d = a; arrival++; } else { departure++; } } if (i < this->vscroll->GetPosition()) { continue; } /* If for some reason the departure is too far in the future or is at a negative time, skip it. */ if ((d->scheduled_date / DAY_TICKS) > (_date + _settings_client.gui.max_departure_time) || d->scheduled_date < 0) { continue; } if (d->terminus == INVALID_STATION) continue; StringID time_str = (departure_types[0] && departure_types[1]) ? (d->type == D_DEPARTURE ? STR_DEPARTURES_TIME_DEP : STR_DEPARTURES_TIME_ARR) : STR_DEPARTURES_TIME; if (_settings_client.gui.departure_show_both) time_str = STR_DEPARTURES_TIME_BOTH; /* Time */ SetDParam(0, d->scheduled_date); SetDParam(1, d->scheduled_date - d->order->wait_time); ltr ? DrawString( text_left, text_left + time_width, y + 1, time_str) : DrawString(text_right - time_width, text_right, y + 1, time_str); /* Vehicle type icon, with thanks to sph */ if (_settings_client.gui.departure_show_vehicle_type) { StringID type = STR_DEPARTURES_TYPE_TRAIN; int offset = (_settings_client.gui.departure_show_vehicle_color ? 1 : 0); switch (d->vehicle->type) { case VEH_TRAIN: type = STR_DEPARTURES_TYPE_TRAIN; break; case VEH_ROAD: type = IsCargoInClass(d->vehicle->cargo_type, CC_PASSENGERS) ? STR_DEPARTURES_TYPE_BUS : STR_DEPARTURES_TYPE_LORRY; break; case VEH_SHIP: type = STR_DEPARTURES_TYPE_SHIP; break; case VEH_AIRCRAFT: type = STR_DEPARTURES_TYPE_PLANE; break; default: break; } type += offset; DrawString(text_left + time_width + 3, text_left + time_width + type_width + 3, y, type); } /* The icons to show with the destination and via stations. */ StringID icon = STR_DEPARTURES_STATION_NONE; StringID icon_via = STR_DEPARTURES_STATION_NONE; if (_settings_client.gui.departure_destination_type) { Station *t = Station::Get(d->terminus.station); if (t->facilities & FACIL_DOCK && t->facilities & FACIL_AIRPORT && d->vehicle->type != VEH_SHIP && d->vehicle->type != VEH_AIRCRAFT) { icon = STR_DEPARTURES_STATION_PORTAIRPORT; } else if (t->facilities & FACIL_DOCK && d->vehicle->type != VEH_SHIP) { icon = STR_DEPARTURES_STATION_PORT; } else if (t->facilities & FACIL_AIRPORT && d->vehicle->type != VEH_AIRCRAFT) { icon = STR_DEPARTURES_STATION_AIRPORT; } } if (_settings_client.gui.departure_destination_type && d->via != INVALID_STATION) { Station *t = Station::Get(d->via); if (t->facilities & FACIL_DOCK && t->facilities & FACIL_AIRPORT && d->vehicle->type != VEH_SHIP && d->vehicle->type != VEH_AIRCRAFT) { icon_via = STR_DEPARTURES_STATION_PORTAIRPORT; } else if (t->facilities & FACIL_DOCK && d->vehicle->type != VEH_SHIP) { icon_via = STR_DEPARTURES_STATION_PORT; } else if (t->facilities & FACIL_AIRPORT && d->vehicle->type != VEH_AIRCRAFT) { icon_via = STR_DEPARTURES_STATION_AIRPORT; } } /* Destination */ if (d->via == INVALID_STATION) { /* Only show the terminus. */ SetDParam(0, d->terminus.station); SetDParam(1, icon); ltr ? DrawString( text_left + time_width + type_width + 6, text_right - status_width - (toc_width + veh_width + group_width + 2) - 2, y + 1, STR_DEPARTURES_TERMINUS) : DrawString(text_left + status_width + (toc_width + veh_width + group_width + 2) + 2, text_right - time_width - type_width - 6, y + 1, STR_DEPARTURES_TERMINUS); } else { /* Show the terminus and the via station. */ SetDParam(0, d->terminus.station); SetDParam(1, icon); SetDParam(2, d->via); SetDParam(3, icon_via); int text_width = (GetStringBoundingBox(STR_DEPARTURES_TERMINUS_VIA_STATION)).width; if (text_width < text_right - status_width - (toc_width + veh_width + group_width + 2) - 2 - (text_left + time_width + type_width + 6)) { /* They will both fit, so show them both. */ SetDParam(0, d->terminus.station); SetDParam(1, icon); SetDParam(2, d->via); SetDParam(3, icon_via); ltr ? DrawString( text_left + time_width + type_width + 6, text_right - status_width - (toc_width + veh_width + group_width + 2) - 2, y + 1, STR_DEPARTURES_TERMINUS_VIA_STATION) : DrawString(text_left + status_width + (toc_width + veh_width + group_width + 2) + 2, text_right - time_width - type_width - 6, y + 1, STR_DEPARTURES_TERMINUS_VIA_STATION); } else { /* They won't both fit, so switch between showing the terminus and the via station approximately every 4 seconds. */ if (this->tick_count & (1 << 7)) { SetDParam(0, d->via); SetDParam(1, icon_via); ltr ? DrawString( text_left + time_width + type_width + 6, text_right - status_width - (toc_width + veh_width + group_width + 2) - 2, y + 1, STR_DEPARTURES_VIA) : DrawString(text_left + status_width + (toc_width + veh_width + group_width + 2) + 2, text_right - time_width - type_width - 6, y + 1, STR_DEPARTURES_VIA); } else { SetDParam(0, d->terminus.station); SetDParam(1, icon); ltr ? DrawString( text_left + time_width + type_width + 6, text_right - status_width - (toc_width + veh_width + group_width + 2) - 2, y + 1, STR_DEPARTURES_TERMINUS_VIA) : DrawString(text_left + status_width + (toc_width + veh_width + group_width + 2) + 2, text_right - time_width - type_width - 6, y + 1, STR_DEPARTURES_TERMINUS_VIA); } } } /* Status */ { int status_left = ltr ? text_right - status_width - 2 - (toc_width + veh_width + group_width + 2) : text_left + (toc_width + veh_width + group_width + 2) + 7; int status_right = ltr ? text_right - (toc_width + veh_width + group_width + 2) + 2 : text_left + status_width + 2 + (toc_width + veh_width + group_width + 7); if (d->status == D_ARRIVED) { /* The vehicle has arrived. */ DrawString(status_left, status_right, y + 1, STR_DEPARTURES_ARRIVED); } else if(d->status == D_CANCELLED) { /* The vehicle has been cancelled. */ DrawString(status_left, status_right, y + 1, STR_DEPARTURES_CANCELLED); } else{ if (d->lateness <= DAY_TICKS && d->scheduled_date > ((_date * DAY_TICKS) + _date_fract)) { /* We have no evidence that the vehicle is late, so assume it is on time. */ DrawString(status_left, status_right, y + 1, STR_DEPARTURES_ON_TIME); } else { if ((d->scheduled_date + d->lateness) < ((_date * DAY_TICKS) + _date_fract)) { /* The vehicle was expected to have arrived by now, even if we knew it was going to be late. */ /* We assume that the train stays at least a day at a station so it won't accidentally be marked as delayed for a fraction of a day. */ DrawString(status_left, status_right, y + 1, STR_DEPARTURES_DELAYED); } else { /* The vehicle is expected to be late and is not yet due to arrive. */ SetDParam(0, d->scheduled_date + d->lateness); DrawString(status_left, status_right, y + 1, STR_DEPARTURES_EXPECTED); } } } } /* Vehicle name */ if (_settings_client.gui.departure_show_vehicle) { SetDParam(0, (uint64)(d->vehicle->index)); ltr ? DrawString(text_right - (toc_width + veh_width + group_width + 2), text_right - toc_width - group_width - 2, y + 1, STR_DEPARTURES_VEH) : DrawString( text_left + toc_width + group_width + 2, text_left + (toc_width + veh_width + group_width + 2), y + 1, STR_DEPARTURES_VEH); } /* Group name */ if (_settings_client.gui.departure_show_group && d->vehicle->group_id != INVALID_GROUP && d->vehicle->group_id != DEFAULT_GROUP) { SetDParam(0, (uint64)(d->vehicle->group_id)); ltr ? DrawString(text_right - (toc_width + group_width + 2), text_right - toc_width - 2, y + 1, STR_DEPARTURES_GROUP) : DrawString( text_left + toc_width + 2, text_left + (toc_width + group_width + 2), y + 1, STR_DEPARTURES_GROUP); } /* Operating company */ if (_settings_client.gui.departure_show_company) { SetDParam(0, (uint64)(d->vehicle->owner)); ltr ? DrawString(text_right - toc_width, text_right, y + 1, STR_DEPARTURES_TOC, TC_FROMSTRING, SA_RIGHT) : DrawString( text_left, text_left + toc_width, y + 1, STR_DEPARTURES_TOC, TC_FROMSTRING, SA_LEFT); } int bottom_y = y + this->entry_height - small_font_size - (_settings_client.gui.departure_larger_font ? 1 : 3); /* Calling at */ ltr ? DrawString( text_left, text_left + calling_at_width, bottom_y, _settings_client.gui.departure_larger_font ? STR_DEPARTURES_CALLING_AT_LARGE : STR_DEPARTURES_CALLING_AT) : DrawString(text_right - calling_at_width, text_right, bottom_y, _settings_client.gui.departure_larger_font ? STR_DEPARTURES_CALLING_AT_LARGE : STR_DEPARTURES_CALLING_AT); /* List of stations */ /* RTL languages can be handled in the language file, e.g. by having the following: */ /* STR_DEPARTURES_CALLING_AT_STATION :{STATION}, {RAW_STRING} */ /* STR_DEPARTURES_CALLING_AT_LAST_STATION :{STATION} & {RAW_STRING}*/ char buffer[512], scratch[512]; if (d->calling_at.Length() != 0) { SetDParam(0, (uint64)(*d->calling_at.Get(0)).station); GetString(scratch, STR_DEPARTURES_CALLING_AT_FIRST_STATION, lastof(scratch)); StationID continuesTo = INVALID_STATION; if (d->calling_at.Get(0)->station == d->terminus.station && d->calling_at.Length() > 1) { continuesTo = d->calling_at.Get(d->calling_at.Length() - 1)->station; } else if (d->calling_at.Length() > 1) { /* There's more than one stop. */ uint i; /* For all but the last station, write out ", <station>". */ for (i = 1; i < d->calling_at.Length() - 1; ++i) { StationID s = d->calling_at.Get(i)->station; if (s == d->terminus.station) { continuesTo = d->calling_at.Get(d->calling_at.Length() - 1)->station; break; } SetDParam(0, (uint64)scratch); SetDParam(1, (uint64)s); GetString(buffer, STR_DEPARTURES_CALLING_AT_STATION, lastof(buffer)); strncpy(scratch, buffer, sizeof(scratch)); } /* Finally, finish off with " and <station>". */ SetDParam(0, (uint64)scratch); SetDParam(1, (uint64)d->calling_at.Get(i)->station); GetString(buffer, STR_DEPARTURES_CALLING_AT_LAST_STATION, lastof(buffer)); strncpy(scratch, buffer, sizeof(scratch)); } SetDParam(0, (uint64)scratch); StringID string; if (continuesTo == INVALID_STATION) { string = _settings_client.gui.departure_larger_font ? STR_DEPARTURES_CALLING_AT_LIST_LARGE : STR_DEPARTURES_CALLING_AT_LIST; } else { SetDParam(1, continuesTo); string = _settings_client.gui.departure_larger_font ? STR_DEPARTURES_CALLING_AT_LIST_SMART_TERMINUS_LARGE : STR_DEPARTURES_CALLING_AT_LIST_SMART_TERMINUS; } GetString(buffer, string, lastof(buffer)); } else { buffer[0] = 0; //SetDParam(0, d->terminus); //GetString(scratch, STR_DEPARTURES_CALLING_AT_FIRST_STATION, lastof(scratch)); } int list_width = (GetStringBoundingBox(buffer, _settings_client.gui.departure_larger_font ? FS_NORMAL : FS_SMALL)).width; /* Draw the whole list if it will fit. Otherwise scroll it. */ if (list_width < text_right - (text_left + calling_at_width + 2)) { ltr ? DrawString(text_left + calling_at_width + 2, text_right, bottom_y, buffer) : DrawString( text_left, text_right - calling_at_width - 2, bottom_y, buffer); } else { DrawPixelInfo tmp_dpi; if (ltr ? !FillDrawPixelInfo(&tmp_dpi, text_left + calling_at_width + 2, bottom_y, text_right - (text_left + calling_at_width + 2), small_font_size + 3) : !FillDrawPixelInfo(&tmp_dpi, text_left , bottom_y, text_right - (text_left + calling_at_width + 2), small_font_size + 3)) { y += this->entry_height; continue; } DrawPixelInfo *old_dpi = _cur_dpi; _cur_dpi = &tmp_dpi; /* The scrolling text starts out of view at the right of the screen and finishes when it is out of view at the left of the screen. */ int pos = ltr ? text_right - (this->tick_count % (list_width + text_right - text_left)) : text_left + (this->tick_count % (list_width + text_right - text_left)); ltr ? DrawString( pos, INT16_MAX, 0, buffer, TC_FROMSTRING, SA_LEFT | SA_FORCE) : DrawString(-INT16_MAX, pos, 0, buffer, TC_FROMSTRING, SA_RIGHT | SA_FORCE); _cur_dpi = old_dpi; } y += this->entry_height; } }
void map::generate_lightmap() { memset(lm, 0, sizeof(lm)); memset(sm, 0, sizeof(sm)); /* Bulk light sources wastefully cast rays into neighbors; a burning hospital can produce significant slowdown, so for stuff like fire and lava: * Step 1: Store the position and luminance in buffer via add_light_source, for efficient checking of neighbors. * Step 2: After everything else, iterate buffer and apply_light_source only in non-redundant directions * Step 3: Profit! */ memset(light_source_buffer, 0, sizeof(light_source_buffer)); const int dir_x[] = { 1, 0 , -1, 0 }; const int dir_y[] = { 0, 1 , 0, -1 }; const int dir_d[] = { 180, 270, 0, 90 }; const float held_luminance = g->u.active_light(); const float natural_light = g->natural_light_level(); if (natural_light > LIGHT_SOURCE_BRIGHT) { // Apply sunlight, first light source so just assign for(int sx = DAYLIGHT_LEVEL - (natural_light / 2); sx < LIGHTMAP_CACHE_X - (natural_light / 2); ++sx) { for(int sy = DAYLIGHT_LEVEL - (natural_light / 2); sy < LIGHTMAP_CACHE_Y - (natural_light / 2); ++sy) { // In bright light indoor light exists to some degree if (!is_outside(sx, sy)) { lm[sx][sy] = LIGHT_AMBIENT_LOW; } else if (g->u.posx == sx && g->u.posy == sy ) { //Only apply daylight on square where player is standing to avoid flooding // the lightmap when in less than total sunlight. lm[sx][sy] = natural_light; } } } } // Apply player light sources if (held_luminance > LIGHT_AMBIENT_LOW) { apply_light_source(g->u.posx, g->u.posy, held_luminance, trigdist); } for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) { for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) { const ter_id terrain = ter(sx, sy); const std::vector<item> &items = i_at(sx, sy); field ¤t_field = field_at(sx, sy); // When underground natural_light is 0, if this changes we need to revisit // Only apply this whole thing if the player is inside, // buildings will be shadowed when outside looking in. if (natural_light > LIGHT_AMBIENT_LOW && !is_outside(g->u.posx, g->u.posy) ) { if (!is_outside(sx, sy)) { // Apply light sources for external/internal divide for(int i = 0; i < 4; ++i) { if (INBOUNDS(sx + dir_x[i], sy + dir_y[i]) && is_outside(sx + dir_x[i], sy + dir_y[i])) { lm[sx][sy] = natural_light; if (light_transparency(sx, sy) > LIGHT_TRANSPARENCY_SOLID) { apply_light_arc(sx, sy, dir_d[i], natural_light); } } } } } for( std::vector<item>::const_iterator itm = items.begin(); itm != items.end(); ++itm ) { float ilum = 0.0; // brightness int iwidth = 0; // 0-360 degrees. 0 is a circular light_source int idir = 0; // otherwise, it's a light_arc pointed in this direction if ( itm->getlight(ilum, iwidth, idir ) ) { if ( iwidth > 0 ) { apply_light_arc( sx, sy, idir, ilum, iwidth ); } else { add_light_source(sx, sy, ilum); } } } if(terrain == t_lava) { add_light_source(sx, sy, 50 ); } if(terrain == t_console) { add_light_source(sx, sy, 3 ); } if(terrain == t_emergency_light) { add_light_source(sx, sy, 3 ); } if(terrain == t_utility_light) { add_light_source(sx, sy, 35 ); } field_entry *cur = NULL; for(std::map<field_id, field_entry *>::iterator field_list_it = current_field.getFieldStart(); field_list_it != current_field.getFieldEnd(); ++field_list_it) { cur = field_list_it->second; if(cur == NULL) { continue; } // TODO: [lightmap] Attach light brightness to fields switch(cur->getFieldType()) { case fd_fire: if (3 == cur->getFieldDensity()) { add_light_source(sx, sy, 160); } else if (2 == cur->getFieldDensity()) { add_light_source(sx, sy, 60); } else { add_light_source(sx, sy, 16); } break; case fd_fire_vent: case fd_flame_burst: add_light_source(sx, sy, 8); break; case fd_electricity: case fd_plasma: if (3 == cur->getFieldDensity()) { add_light_source(sx, sy, 8); } else if (2 == cur->getFieldDensity()) { add_light_source(sx, sy, 1); } else { apply_light_source(sx, sy, LIGHT_SOURCE_LOCAL, trigdist); // kinda a hack as the square will still get marked } break; case fd_incendiary: if (3 == cur->getFieldDensity()) { add_light_source(sx, sy, 30); } else if (2 == cur->getFieldDensity()) { add_light_source(sx, sy, 16); } else { add_light_source(sx, sy, 8); } break; case fd_laser: apply_light_source(sx, sy, 1, trigdist); break; case fd_spotlight: add_light_source(sx, sy, 20); break; case fd_dazzling: add_light_source(sx, sy, 2); break; default: //Suppress warnings break; } } } } for (size_t i = 0; i < g->num_zombies(); ++i) { int mx = g->zombie(i).posx(); int my = g->zombie(i).posy(); if (INBOUNDS(mx, my)) { if (g->zombie(i).has_effect("onfire")) { apply_light_source(mx, my, 3, trigdist); } // TODO: [lightmap] Attach natural light brightness to creatures // TODO: [lightmap] Allow creatures to have light attacks (ie: eyebot) // TODO: [lightmap] Allow creatures to have facing and arc lights if (g->zombie(i).type->luminance > 0) { apply_light_source(mx, my, g->zombie(i).type->luminance, trigdist); } } } // Apply any vehicle light sources VehicleList vehs = get_vehicles(); for( size_t v = 0; v < vehs.size(); ++v ) { if(vehs[v].v->lights_on) { int dir = vehs[v].v->face.dir(); float veh_luminance = 0.0; float iteration = 1.0; std::vector<int> light_indices = vehs[v].v->all_parts_with_feature(VPFLAG_CONE_LIGHT); for (std::vector<int>::iterator part = light_indices.begin(); part != light_indices.end(); ++part) { veh_luminance += ( vehs[v].v->part_info(*part).bonus / iteration ); iteration = iteration * 1.1; } if (veh_luminance > LL_LIT) { for (std::vector<int>::iterator part = light_indices.begin(); part != light_indices.end(); ++part) { int px = vehs[v].x + vehs[v].v->parts[*part].precalc_dx[0]; int py = vehs[v].y + vehs[v].v->parts[*part].precalc_dy[0]; if(INBOUNDS(px, py)) { apply_light_arc(px, py, dir + vehs[v].v->parts[*part].direction, veh_luminance, 45); } } } } if(vehs[v].v->overhead_lights_on) { std::vector<int> light_indices = vehs[v].v->all_parts_with_feature(VPFLAG_CIRCLE_LIGHT); for (std::vector<int>::iterator part = light_indices.begin(); part != light_indices.end(); ++part) { if((calendar::turn % 2 && vehs[v].v->part_info(*part).has_flag(VPFLAG_ODDTURN)) || (!(calendar::turn % 2) && vehs[v].v->part_info(*part).has_flag(VPFLAG_EVENTURN)) || (!vehs[v].v->part_info(*part).has_flag(VPFLAG_EVENTURN) && !vehs[v].v->part_info(*part).has_flag(VPFLAG_ODDTURN))) { int px = vehs[v].x + vehs[v].v->parts[*part].precalc_dx[0]; int py = vehs[v].y + vehs[v].v->parts[*part].precalc_dy[0]; if(INBOUNDS(px, py)) { add_light_source( px, py, vehs[v].v->part_info(*part).bonus ); } } } } } /* Now that we have position and intensity of all bulk light sources, apply_ them This may seem like extra work, but take a 12x12 raging inferno: unbuffered: (12^2)*(160*4) = apply_light_ray x 92160 buffered: (12*4)*(160) = apply_light_ray x 7680 */ for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) { for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) { if ( light_source_buffer[sx][sy] > 0. ) { apply_light_source(sx, sy, light_source_buffer[sx][sy], ( trigdist && light_source_buffer[sx][sy] > 3. ) ); } } } if (g->u.has_active_bionic("bio_night") ) { for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) { for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) { if (rl_dist(sx, sy, g->u.posx, g->u.posy) < 15) { lm[sx][sy] = 0; } } } } }
void map::generate_lightmap(game* g) { memset(lm, 0, sizeof(lm)); memset(sm, 0, sizeof(sm)); const int dir_x[] = { 1, 0 , -1, 0 }; const int dir_y[] = { 0, 1 , 0, -1 }; const int dir_d[] = { 180, 270, 0, 90 }; const float luminance = g->u.active_light(); const float natural_light = g->natural_light_level(); // Daylight vision handling returned back to map due to issues it causes here if (natural_light > LIGHT_SOURCE_BRIGHT) { // Apply sunlight, first light source so just assign for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) { for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) { // In bright light indoor light exists to some degree if (!g->m.is_outside(sx, sy)) lm[sx][sy] = LIGHT_AMBIENT_LOW; } } } // Apply player light sources if (luminance > LIGHT_AMBIENT_LOW) apply_light_source(g->u.posx, g->u.posy, luminance); for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) { for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) { const ter_id terrain = g->m.ter(sx, sy); const std::vector<item> items = g->m.i_at(sx, sy); const field current_field = g->m.field_at(sx, sy); // When underground natural_light is 0, if this changes we need to revisit if (natural_light > LIGHT_AMBIENT_LOW) { if (!g->m.is_outside(sx, sy)) { // Apply light sources for external/internal divide for(int i = 0; i < 4; ++i) { if (INBOUNDS(sx + dir_x[i], sy + dir_y[i]) && g->m.is_outside(sx + dir_x[i], sy + dir_y[i])) { if (INBOUNDS(sx, sy) && g->m.is_outside(0, 0)) lm[sx][sy] = natural_light; if (g->m.light_transparency(sx, sy) > LIGHT_TRANSPARENCY_SOLID) apply_light_arc(sx, sy, dir_d[i], natural_light); } } } } if (items.size() == 1 && items[0].type->id == itm_flashlight_on) apply_light_source(sx, sy, 20); if(terrain == t_lava) apply_light_source(sx, sy, 50); if(terrain == t_console) apply_light_source(sx, sy, 3); if (items.size() == 1 && items[0].type->id == itm_candle_lit) apply_light_source(sx, sy, 4); if(terrain == t_emergency_light) apply_light_source(sx, sy, 3); // TODO: [lightmap] Attach light brightness to fields switch(current_field.type) { case fd_fire: if (3 == current_field.density) apply_light_source(sx, sy, 160); else if (2 == current_field.density) apply_light_source(sx, sy, 60); else apply_light_source(sx, sy, 16); break; case fd_fire_vent: case fd_flame_burst: apply_light_source(sx, sy, 8); break; case fd_electricity: if (3 == current_field.density) apply_light_source(sx, sy, 8); else if (2 == current_field.density) apply_light_source(sx, sy, 1); else apply_light_source(sx, sy, LIGHT_SOURCE_LOCAL); // kinda a hack as the square will still get marked break; } } } for (int i = 0; i < g->z.size(); ++i) { int mx = g->z[i].posx; int my = g->z[i].posy; if (INBOUNDS(mx, my)) { if (g->z[i].has_effect(ME_ONFIRE)) { apply_light_source(mx, my, 3); } // TODO: [lightmap] Attach natural light brightness to creatures // TODO: [lightmap] Allow creatures to have light attacks (ie: eyebot) // TODO: [lightmap] Allow creatures to have facing and arc lights switch (g->z[i].type->id) { case mon_zombie_electric: apply_light_source(mx, my, 1); break; case mon_turret: apply_light_source(mx, my, 2); break; case mon_flaming_eye: apply_light_source(mx, my, LIGHT_SOURCE_BRIGHT); break; case mon_manhack: apply_light_source(mx, my, LIGHT_SOURCE_LOCAL); break; } } } // Apply any vehicle light sources VehicleList vehs = g->m.get_vehicles(); for(int v = 0; v < vehs.size(); ++v) { if(vehs[v].v->lights_on) { int dir = vehs[v].v->face.dir(); for (std::vector<int>::iterator part = vehs[v].v->external_parts.begin(); part != vehs[v].v->external_parts.end(); ++part) { int px = vehs[v].x + vehs[v].v->parts[*part].precalc_dx[0]; int py = vehs[v].y + vehs[v].v->parts[*part].precalc_dy[0]; if(INBOUNDS(px, py)) { int dpart = vehs[v].v->part_with_feature(*part , vpf_light); if (dpart >= 0) { float luminance = vehs[v].v->part_info(dpart).power; if (luminance > LL_LIT) { apply_light_arc(px, py, dir, luminance); } } } } } } }
void map::generate_lightmap(game* g) { memset(lm, 0, sizeof(lm)); memset(sm, 0, sizeof(sm)); const int dir_x[] = { 1, 0 , -1, 0 }; const int dir_y[] = { 0, 1 , 0, -1 }; const int dir_d[] = { 180, 270, 0, 90 }; const float held_luminance = g->u.active_light(); const float natural_light = g->natural_light_level(); // Daylight vision handling returned back to map due to issues it causes here if (natural_light > LIGHT_SOURCE_BRIGHT) { // Apply sunlight, first light source so just assign for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) { for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) { // In bright light indoor light exists to some degree if (!g->m.is_outside(sx, sy)) { lm[sx][sy] = LIGHT_AMBIENT_LOW; } else if (g->u.posx == sx && g->u.posy == sy ) { //Only apply daylight on square where player is standing to avoid flooding // the lightmap when in less than total sunlight. lm[sx][sy] = natural_light; } } } } // Apply player light sources if (held_luminance > LIGHT_AMBIENT_LOW) apply_light_source(g->u.posx, g->u.posy, held_luminance, trigdist); int flood_basalt_check = 0; // does excessive lava need high quality lighting? Nope nope nope nope for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) { for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) { const ter_id terrain = g->m.ter(sx, sy); const std::vector<item> &items = g->m.i_at(sx, sy); field ¤t_field = g->m.field_at(sx, sy); // When underground natural_light is 0, if this changes we need to revisit if (natural_light > LIGHT_AMBIENT_LOW) { if (!g->m.is_outside(sx, sy)) { // Apply light sources for external/internal divide for(int i = 0; i < 4; ++i) { if (INBOUNDS(sx + dir_x[i], sy + dir_y[i]) && g->m.is_outside(sx + dir_x[i], sy + dir_y[i])) { if (INBOUNDS(sx, sy) && g->m.is_outside(0, 0)) lm[sx][sy] = natural_light; if (g->m.light_transparency(sx, sy) > LIGHT_TRANSPARENCY_SOLID) apply_light_arc(sx, sy, dir_d[i], natural_light); } } } } for( std::vector<item>::const_iterator itm = items.begin(); itm != items.end(); ++itm ) { if ( itm->has_flag("LIGHT_20")) { apply_light_source(sx, sy, 20, trigdist); } if ( itm->has_flag("LIGHT_1")) { apply_light_source(sx, sy, 1, trigdist); } if ( itm->has_flag("LIGHT_4")) { apply_light_source(sx, sy, 4, trigdist); } if ( itm->has_flag("LIGHT_8")) { apply_light_source(sx, sy, 8, trigdist); } } if(terrain == t_lava) { flood_basalt_check++; apply_light_source(sx, sy, 50, trigdist && flood_basalt_check < 512 ); // todo: optimize better } if(terrain == t_console) apply_light_source(sx, sy, 3, false); // 3^2 circle is just silly if(terrain == t_emergency_light) apply_light_source(sx, sy, 3, false); field_entry *cur = NULL; for(std::map<field_id, field_entry*>::iterator field_list_it = current_field.getFieldStart(); field_list_it != current_field.getFieldEnd(); ++field_list_it){ cur = field_list_it->second; if(cur == NULL) continue; // TODO: [lightmap] Attach light brightness to fields switch(cur->getFieldType()) { case fd_fire: if (3 == cur->getFieldDensity()) apply_light_source(sx, sy, 160, trigdist); else if (2 == cur->getFieldDensity()) apply_light_source(sx, sy, 60, trigdist); else apply_light_source(sx, sy, 16, trigdist); break; case fd_fire_vent: case fd_flame_burst: apply_light_source(sx, sy, 8, trigdist); break; case fd_electricity: case fd_plasma: if (3 == cur->getFieldDensity()) apply_light_source(sx, sy, 8, trigdist); else if (2 == cur->getFieldDensity()) apply_light_source(sx, sy, 1, trigdist); else apply_light_source(sx, sy, LIGHT_SOURCE_LOCAL, trigdist); // kinda a hack as the square will still get marked break; case fd_laser: apply_light_source(sx, sy, 1, trigdist); break; } } } } for (int i = 0; i < g->num_zombies(); ++i) { int mx = g->zombie(i).posx(); int my = g->zombie(i).posy(); if (INBOUNDS(mx, my)) { if (g->zombie(i).has_effect(ME_ONFIRE)) { apply_light_source(mx, my, 3, trigdist); } // TODO: [lightmap] Attach natural light brightness to creatures // TODO: [lightmap] Allow creatures to have light attacks (ie: eyebot) // TODO: [lightmap] Allow creatures to have facing and arc lights switch (g->zombie(i).type->id) { case mon_zombie_electric: apply_light_source(mx, my, 1, trigdist); break; case mon_turret: apply_light_source(mx, my, 2, trigdist); break; case mon_flaming_eye: apply_light_source(mx, my, LIGHT_SOURCE_BRIGHT, trigdist); break; case mon_manhack: apply_light_source(mx, my, LIGHT_SOURCE_LOCAL, trigdist); break; } } } // Apply any vehicle light sources VehicleList vehs = g->m.get_vehicles(); for(int v = 0; v < vehs.size(); ++v) { if(vehs[v].v->lights_on) { int dir = vehs[v].v->face.dir(); float veh_luminance=0.0; float iteration=1.0; for (std::vector<int>::iterator part = vehs[v].v->external_parts.begin(); part != vehs[v].v->external_parts.end(); ++part) { int dpart = vehs[v].v->part_with_feature(*part , "LIGHT"); if (dpart >= 0) { veh_luminance += ( vehs[v].v->part_info(dpart).power / iteration ); iteration=iteration * 1.1; } } if (veh_luminance > LL_LIT) { for (std::vector<int>::iterator part = vehs[v].v->external_parts.begin(); part != vehs[v].v->external_parts.end(); ++part) { int px = vehs[v].x + vehs[v].v->parts[*part].precalc_dx[0]; int py = vehs[v].y + vehs[v].v->parts[*part].precalc_dy[0]; if(INBOUNDS(px, py)) { int dpart = vehs[v].v->part_with_feature(*part , "LIGHT"); if (dpart >= 0) { apply_light_arc(px, py, dir + vehs[v].v->parts[dpart].direction, veh_luminance, 45); } } } } } } if (g->u.has_active_bionic("bio_night") ) { for(int sx = 0; sx < LIGHTMAP_CACHE_X; ++sx) { for(int sy = 0; sy < LIGHTMAP_CACHE_Y; ++sy) { if (rl_dist(sx, sy, g->u.posx, g->u.posy) < 15) { lm[sx][sy] = 0; } } } } }