Exemple #1
0
void game_reset_gamedata(GameData* game)
{
  game->current = 0;
  game->magic = SAVE_MAGIC;
  game->version = 0;

  item_shuffleTypes((int*)game->charmTypes, CHARM_MAX);
  item_shuffleTypes((int*)game->conchTypes, CONCH_MAX);

  int i;
  for(i=0; i<MAX_FATHOMS; i++)
  {
    int j;
    game->fathoms[i] = game_null_fathomdata();
    feature_process(game, &game->fathoms[i], i);
    int threat = 7+i*4;
    if(i==MAX_FATHOMS-1)
      threat *= 2;
    while(threat > 0)
    {
      int type = ET_MAX_ENEMY;
      while(type >= ET_MAX_ENEMY)
      {
        type = 0;
        for(j=0; j<(i*2)/3+2; j++)
          type += sys_randint(2);
      }
      Entity e = spawn_entity(type);
      if(e.flags & EF_TOOLED)
      {
        Item i = spawn_item(game, sys_randint(IT_MAX-1));
        i.active = true;
        if(i.type == IT_CHARM)
          i.worn = true;
        if(i.type != IT_CONCH || i.conchSubtype != CONCH_DEATH) // death isn't fair
          e.inventory[0] = i;
      }
      game_spawn(&game->fathoms[i], e);
      threat -= type+1;
    }    
    for(j=0; j<(MAX_FATHOMS-i)/4+4; j++)
      game_spawn(&game->fathoms[i], spawn_entity(ET_BUBBLE));
    int numSpawns;
    if(sys_randint(3)==0)
      game_place(&game->fathoms[i], spawn_item(game, IT_CONCH));
    if(sys_randint(5)==0)
      game_place(&game->fathoms[i], spawn_item(game, IT_CHARM));
    numSpawns = (i == MAX_FATHOMS-1) ? 5 : 0;
    for(j=0; j<numSpawns; j++)
      game_place(&game->fathoms[i], spawn_item(game, IT_DOUBLOON));
  }
  game_spawn(&game->fathoms[0], spawn_entity(ET_SCUBA));    

}
Exemple #2
0
void GameInit() {
	g_game_rand = random(GetTickCount());

	g_game._player = spawn_entity(&g_game, new player(), vec2(10.0f, 9.5f));

	for(int j = 0; j < MAP_HEIGHT; j++) {
		for(int i = 0; i < MAP_WIDTH; i++) {
			if (tile* t = g_game.get(i, j)) {
				if (j < 10) {
					t->type =  ((i == 0) || (i == MAP_WIDTH - 1)) ? TT_VOID : TT_EMPTY;
				}
				else if (j < (MAP_HEIGHT - 2)) {
					t->type = ((i == 0) || (i == MAP_WIDTH - 1)) ? TT_WALL : TT_SOLID;

					if ((i > 0) && (i < MAP_WIDTH - 1)) {
						int wall_c = max(20 - ((j - 10) / 2), 5);

						if (g_game_rand.rand(0, 150) == 0) {
							if (j > 20)
								t->ore = -1;
						}
						else if (g_game_rand.rand(0, wall_c) == 0) {
							t->type = TT_WALL;
						}
					}
				}
				else
					t->type = TT_WALL;
			}
		}

		int ores = clamp(j / 15, 1, 10) + g_game_rand.rand(0, 2);
		
		for(int z = 0; z < ores; z++) {
			int i = g_game_rand.rand(1, MAP_WIDTH - 2);

			if (tile* t = g_game.get(i, j)) {
				if (t->type == TT_SOLID) {
					if (t->ore == 0) {
						t->ore = 1;

						for(int z = j / 15; z > 0; z--)
							t->ore += g_game_rand.rand(0, 4) == 0;
					}
				}
			}
		}
	}
}
Exemple #3
0
void ProductionSystem::update(tdt::real delta)
{
	for(auto& ent : entities_.get_component_container<ProductionComponent>())
	{
		if(ent.second.curr_produced >= ent.second.max_produced)
			continue;

		if(ent.second.curr_cd < ent.second.cooldown)
			ent.second.curr_cd += delta * time_multiplier_;
		else
		{
			spawn_entity(ent.first, ent.second.product_blueprint);
			++ent.second.curr_produced;
			ent.second.curr_cd = REAL_ZERO;
		}
	}
}
Exemple #4
0
void GameUpdate() {
	game* g = &g_game;

	if (is_key_pressed(KEY_RESET)) {
		memset(g->_map, 0, sizeof(g->_map));

		g->_entities.free();
		g->_player = 0;

		g->_cam_pos = vec2(MAP_WIDTH * 0.5f, 8.5f);
		g->_target_cam_y = 8.5f;

		g->_diff = 1;
		g->_diff_dmg = 1;
		g->_spawn_time = 800;
		g->_spawn_count = 6;
		g->_wave_incoming = true;
		g->_plr_dmg = 1;

		GameInit();

		g_title = true;
	}

	if (g_title) {
		set_camera(vec2(), 10.0f);

		float ratio = g_WinSize.y / (float)g_WinSize.x;
		vec2 orig(-10.0f, -10.0f * ratio);

		draw_string(vec2(0.0f, -4.75), 0.15f, TEXT_CENTRE, colour(0.5f, 0.5f, 1.0f, 1.0f), "Tunnel Defense");

		float y = -3.25f;

		draw_string(vec2(0.0f, y), 0.05f, TEXT_CENTRE, colour(0.3f, 0.3f, 0.6f, 1.0f), "Originally made for LD29 - post competition version"); y += 0.5f;

		y += 0.5f;

		draw_string(vec2(0.0f, y), 0.05f, TEXT_CENTRE, colour(0.6f, 0.3f, 0.3f, 1.0f), "Dig for your life, the creeps are coming and the"); y += 0.5f;
		draw_string(vec2(0.0f, y), 0.05f, TEXT_CENTRE, colour(0.6f, 0.3f, 0.3f, 1.0f), "only safe place is underground! Collect resources"); y += 0.5f;
		draw_string(vec2(0.0f, y), 0.05f, TEXT_CENTRE, colour(0.6f, 0.3f, 0.3f, 1.0f), "and build turrets to protect yourself."); y += 0.5f;

		y += 0.25f;

		float r = 5.0f;

		draw_string(vec2(-r, y), 0.05f, TEXT_LEFT, colour(0.6f, 0.6f, 0.6f, 1.0f), "\001\002\003\004");
		draw_string(vec2(r, y), 0.05f, TEXT_RIGHT, colour(0.3f, 0.6f, 0.3f, 1.0f), "Move + Aim"); y += 0.5f;
		draw_string(vec2(-r, y), 0.05f, TEXT_LEFT, colour(0.6f, 0.6f, 0.6f, 1.0f), "%c", g_LocKeyZ);
		draw_string(vec2(r, y), 0.05f, TEXT_RIGHT, colour(0.3f, 0.6f, 0.3f, 1.0f), "Jump"); y += 0.5f;
		draw_string(vec2(-r, y), 0.05f, TEXT_LEFT, colour(0.6f, 0.6f, 0.6f, 1.0f), "%c", g_LocKeyX);
		draw_string(vec2(r, y), 0.05f, TEXT_RIGHT, colour(0.3f, 0.6f, 0.3f, 1.0f), "Dig block / shoot"); y += 0.5f;
		draw_string(vec2(-r, y), 0.05f, TEXT_LEFT, colour(0.6f, 0.6f, 0.6f, 1.0f), "%c", g_LocKeyC);
		draw_string(vec2(r, y), 0.05f, TEXT_RIGHT, colour(0.3f, 0.6f, 0.3f, 1.0f), "Build / upgrade turret"); y += 0.5f;

		y += 0.25f;

		draw_string(vec2(0.0f, y), 0.05f, TEXT_CENTRE, colour(0.15f, 0.3f, 0.15f, 1.0f), "Alternate controls - %c%c%c%c, %c, %c, %c", g_LocKeyW, g_LocKeyA, g_LocKeyS, g_LocKeyD, g_LocKeyI, g_LocKeyO, g_LocKeyP); y += 0.5f;

		y += 0.25f;

		draw_string(vec2(0.0f, y), 0.05f, TEXT_CENTRE, colour(0.3f, 0.15f, 0.3f, 1.0f), "Building / upgrading a turret costs 3 metal."); y += 0.5f;
		draw_string(vec2(0.0f, y), 0.05f, TEXT_CENTRE, colour(0.3f, 0.15f, 0.3f, 1.0f), "Building a turret increases your shot power."); y += 0.5f;

		y += 0.25f;

		draw_string(vec2(0.0f, y), 0.05f, TEXT_CENTRE, colour(0.5f, 0.5f, 0.5f, 1.0f), "press SPACE to START"); y+= 0.5f;

		y += 0.5f;

		draw_string(vec2(0.0f, y), 0.035f, TEXT_CENTRE, colour(0.15f, 0.15f, 0.15f, 1.0f), "by Stephen Cakebread @quantumrain");

		if (is_key_pressed(KEY_FIRE) || is_key_pressed(KEY_ALT_FIRE)) g_title = false;

		return;
	}

	if (player* p = g->_player) {
		vec2 target_cam_pos = vec2(MAP_WIDTH * 0.5f, p->centre().y);

		float dy = target_cam_pos.y - g->_target_cam_y;

		float hyst = 1.25f;

		if (fabsf(dy) > hyst) {
			if (dy > 0.0f)
				g->_target_cam_y += dy - hyst;
			else
				g->_target_cam_y += dy + hyst;
		}

		target_cam_pos.y = g->_target_cam_y;

		update_search(g, to_ivec2(p->centre()));

		g->_cam_pos = lerp(g->_cam_pos, target_cam_pos, 0.2f);

		g->_plr_dmg = 1;

		for(uint32_t i = 0; i < g->_entities.size(); i++) {
			entity* e = g->_entities[i];
			if (!(e->_flags & entity::FLAG_DESTROYED)) {
				if (e->_type == ET_TURRET)
					g->_plr_dmg++;
			}
		}
	}

	set_camera(g->_cam_pos, 15.0f);

	if (g->_player) {
		if (--g->_spawn_time <= 0) {
			if (g->_wave_incoming) {
				g->_spawn_count = 4 + 4 * (int)g->_diff;
				g->_diff_dmg = (int)(2.5f * (1 + g->_diff)) - 1;
			}

			g->_wave_incoming = false;

			vec2 pos(g_game_rand.frand(1.0f, MAP_WIDTH - 1.0f), 2.0f);

			if (bug* e = spawn_entity(&g_game, new bug(), pos)) {
				e->_max_damage = g->_diff_dmg;

				if (g_game_rand.rand(0, 5) == 0) e->_max_damage += g_game_rand.rand(0, g->_diff_dmg);
				if (g_game_rand.rand(0, 15) == 0) e->_max_damage += g_game_rand.rand(0, g->_diff_dmg);
				if (g_game_rand.rand(0, 30) == 0) e->_max_damage += g_game_rand.rand(0, g->_diff_dmg);
			}

			g->_spawn_time = clamp(60 - ((g->_diff * 5) / 4), 10, 60);

			if (--g->_spawn_count <= 0){
				g->_diff += 1 + (g->_diff / 5);
				g->_spawn_time = 600;
				g->_wave_incoming = true;
			}
		}
	}

	tick_entities(g);
	purge_entities(g);
	update_particles(g);

	colour sky0(0.4f, 0.7f, 1.0f, 1.0f);
	colour sky1(0.45f, 0.75f, 1.0f, 1.0f);
	colour sky2(0.3f, 0.6f, 0.9f, 1.0f);
	colour sky3(0.35f, 0.65f, 0.9f, 1.0f);
	
	sky0 *= colour(0.65f, 0.0f);
	sky1 *= colour(0.65f, 0.0f);
	sky2 *= colour(0.35f, 1.0f);
	sky3 *= colour(0.35f, 1.0f);

	float sky_space = 4.5f;
	float sky_top = 4.75f;
	float sky_y = 10.0f;
	float sky_yf = 10.5f;
	float sky_yb = 11.0f;

	for(int j = 0; j < MAP_HEIGHT; j++) {
		colour bk_colour(get_master_colour(j) * colour(0.2f, 1.0f));

		for(int i = 0; i < MAP_WIDTH; i++) {
			int t = g->get_tile(i, j);

			if (t == TT_EMPTY || t == TT_TURRET) {
				int hash = ((i * 7) ^ (j * 3)) + (i + j);
				hash ^= ((hash >> 3) * 9);

				int tile_num = 132 + (hash % 4);

				draw_tile(vec2((float)i, (float)j), vec2(i + 1.0f, j + 1.0f), bk_colour, tile_num, 0);
			}
		}
	}
Exemple #5
0
void _do_fire(GameData* game, Entity* e, int index, Direction direction)
{
  FathomData* fathom = &game->fathoms[game->current];
  Item* item = &e->inventory[index];
  Point vector = directionToPoint(direction);
  Point pos = pointAddPoint(e->pos, vector);
  int distance = 3 + sys_randint(3);
  int i;

  Entity nullEntity = NULL_ENTITY;

  switch(item->conchSubtype)
  {
    case CONCH_BUBBLE:
    {
      int spawnType = sys_randint(ET_MAX_ENEMY);
      for(i=0; i<distance; i++)
      {
        if(sys_randint(4)==0)
          game_spawnAt(fathom, spawn_entity(spawnType), pos);
        else
          game_spawnAt(fathom, spawn_entity(ET_BUBBLE), pos);
        pos = pointAddPoint(pos, vector);
      }
      break;
    }
    case CONCH_DIG:
    {
      Tile nullTile = NULL_TILE;
      for(i=0; i<distance; i++)
      {
        int index = tilemap_indexFromTilePosition(&fathom->tileMap, pos);
        if(index != -1)
          fathom->tileMap.tiles[index] = nullTile;
        pos = pointAddPoint(pos, vector);
      }
      break;
    }
    case CONCH_JUMP:
    {
      pos = pointAddPoint(pos, pointMultiply(vector, distance));
      Point invert = pointInverse(vector);
      for(i=distance-1; i>0; i--)
      {          
        if(game_pointFree(fathom, pos))
        {
          e->pos = pos;
          break;
        }
        pos = pointAddPoint(pos, invert);
      }
      break;
    }
    case CONCH_DEATH:
    {
      if(e->o2 > 4)
        e->o2 = e->o2/4;
      for(i=0; i<distance; i++)
      {
        int index = game_pointEntityIndex(fathom, pos);
        if(index != -1)
          fathom->entities[index] = nullEntity;
        pos = pointAddPoint(pos, vector);          
      }
      break;
    }
    case CONCH_POLYMORPH:
    {
      for(i=0; i<distance; i++)
      {
        int index = game_pointEntityIndex(fathom, pos);
        if(index != -1)
        {
          bool player = fathom->entities[index].player;
          fathom->entities[index] = nullEntity;
          Entity e = spawn_entity(sys_randint(ET_MAX_ENEMY));
          e.player = player;
          game_spawnAt(fathom, e, pos);
        }
        int j;
        for(j=0; j<MAX_ITEMS; j++)
        {
          Item* item = &fathom->items[j];
          if(!item->active)
            continue;
          if(pos.x != item->pos.x || pos.y != item->pos.y)
            continue;
          *item = spawn_item(game, item->type);
          item->pos = pos;
          item->active = true;
        }
        pos = pointAddPoint(pos, vector);
      }
      break;
    }
    case CONCH_MAPPING:
    {
      for(i=0; i<fathom->tileMap.size.x*fathom->tileMap.size.y; i++)
        fathom->tileMap.tiles[i].seen = true;
      break;
    }
    default:
      LOG("Trying to cast invalid conch.");
      break;
  }
  game_addMessage(fathom, e->pos, "%s fired %s %s.", _getName(e->name), item_subtypeDescription(item->subtype), item_typeName(item->type));
  if(sys_randint(5)==0)
  {
    Item nullItem = NULL_ITEM;
    game_addMessage(fathom, e->pos, "the %s %s falls apart.", item_subtypeDescription(item->subtype), item_typeName(item->type));
    *item = nullItem;
  }
}
Exemple #6
0
void game_update_and_render(RenderQueue* render_queue, Game* game, Input* input,
                            u64 dt) {
    if (game->entities.entity_count == 0) {
        V2i pos;
        pos.x = 7;
        pos.y = 5;
        u32 id = spawn_entity(&game->entities);
        register_location(&game->entities, id, pos, SOUTH);
        register_appearance(&game->entities, id, 7723);
        register_controlled(&game->entities, id);
    }

    // Handle user input

    if (input->keys[ESCAPE] != 0) {
        game->should_end = true;
    }
    u32 entity_to_control_mask_include =
        ENTITY_LOCATED_BIT | ENTITY_CONTROLLED_BIT;
    u32 entity_to_control_mask_exclude = ENTITY_MOVING_BIT;
    u32 entity_to_control_count = 0;
    u32 entity_to_control[MAX_ENTITY_COUNT];
    filter_entities(&game->entities, entity_to_control_mask_include,
                    entity_to_control_mask_exclude, entity_to_control,
                    &entity_to_control_count);
    V2i positions[MAX_ENTITY_COUNT];
    for (u32 i = 0; i < entity_to_control_count; i++) {
        for (u32 j = 0; j < game->entities.located_count; j++) {
            if (entity_to_control[i] == game->entities.located[j]) {
                positions[i] = game->entities.positions[j];
                break;
            }
        }
    }
    bool moved = false;
    Direction direction = NORTH;
    V2i destination_diff;
    for (u32 i = 1; i < 5; i++) {
        i32 key_state = input->keys[i] + game->previous_key_state[i];
        if (key_state && entity_to_control_count > 0) {
            moved = true;
            destination_diff.x = 0;
            destination_diff.y = 0;
            switch (i) {
            case W:
                direction = NORTH;
                destination_diff.y -= 1;
                break;
            case A:
                direction = WEST;
                destination_diff.x -= 1;
                break;
            case S:
                direction = SOUTH;
                destination_diff.y += 1;
                break;
            case D:
                direction = EAST;
                destination_diff.x += 1;
                break;
            default:
                assert(!(bool)"Unreachable!");
                break;
            }
        }
        game->previous_key_state[i] = key_state % 2;
    }
    if (moved) {
        for (u32 j = 0; j < entity_to_control_count; j++) {
            f32 distance = 0.0f;
            V2i destination;
            destination.x = positions[j].x + destination_diff.x;
            destination.y = positions[j].y + destination_diff.y;
            u32 id = entity_to_control[j];
            register_moving(&game->entities, id, destination, distance);
            update_located_direction(&game->entities, id, direction);
        }
    }

    // Update

    game->tick += dt;
    if (game->tick > MS_TO_NS(2000)) {
        game->tick = 0;
        for (int y = 0; y < 11; y++) {
            for (int x = 0; x < 15; x++) {
                game->tiles[x][y] = rand() % 101925;
            }
        }
    }

    { // Update moving entities
        u32 movable_mask = ENTITY_LOCATED_BIT | ENTITY_MOVING_BIT;
        u32 entities_to_move_count = 0;
        u32 entities_to_move[MAX_ENTITY_COUNT];
        filter_entities(&game->entities, movable_mask, 0, entities_to_move,
                        &entities_to_move_count);

        u32 not_moving_count = 0;
        u32 not_moving[MAX_ENTITY_COUNT];
        for (u32 i = 0; i < entities_to_move_count; i++) {
            u32 id = entities_to_move[i];

            u32 located_index = 0;
            for (u32 j = 0; j < game->entities.located_count; j++) {
                if (game->entities.located[j] == id) {
                    located_index = j;
                    break;
                }
            }

            u32 moving_index = 0;
            for (u32 j = 0; j < game->entities.moving_count; j++) {
                if (game->entities.moving[j] == id) {
                    moving_index = j;
                    break;
                }
            }

            if (game->entities.distances[moving_index] >= 1.0f) {
                game->entities.positions[located_index] =
                    game->entities.destinations[moving_index];
                game->entities.distances[moving_index] = 0.0f;
                not_moving[not_moving_count] = id;
                not_moving_count += 1;
            } else {
                game->entities.distances[moving_index] += 0.1f;
            }
        }

        for (u32 i = 0; i < not_moving_count; i++) {
            deregister_moving(&game->entities, not_moving[i]);
        }
    }

    // Render

    render_queue_push_clear(render_queue, 0.0f, 0.0f, 0.0f, 0.0f);

    game->camera_x = 7.0f;
    game->camera_y = 5.0f;

    u32 tile_size = 64;
    for (u32 r = 0; r < 128; r++) {
        for (u32 c = 0; c < 128; c++) {
            f32 x = c - game->camera_x + 7;
            f32 y = r - game->camera_y + 5;
            if (0.0f <= x && x < 16.0f && 0 <= y && y < 12.0f) {
                render_queue_push_sprite(render_queue, game->world[r][c],
                                         (i32)(x * tile_size + tile_size),
                                         (i32)(y * tile_size + tile_size),
                                         tile_size);
            }
        }
    }

    u32 drawable_mask = ENTITY_LOCATED_BIT | ENTITY_APPEARANCE_BIT;
    u32 entities_to_draw_count = 0;
    u32 entities_to_draw[MAX_ENTITY_COUNT];
    filter_entities(&game->entities, drawable_mask, 0, entities_to_draw,
                    &entities_to_draw_count);

    u32 ms[MAX_ENTITY_COUNT];
    for (u32 i = 0; i < entities_to_draw_count; i++) {
        for (u32 j = 0; j < game->entities.entity_count; j++) {
            if (entities_to_draw[i] == game->entities.ids[j]) {
                assert((game->entities.masks[j] & drawable_mask) ==
                       drawable_mask);
                ms[i] = game->entities.masks[j];
                break;
            }
        }
    }
    u32 ss[MAX_ENTITY_COUNT];
    for (u32 i = 0; i < entities_to_draw_count; i++) {
        u32 id = entities_to_draw[i];
        for (u32 j = 0; j < game->entities.appearance_count; j++) {
            if (game->entities.appearance[j] == id) {
                ss[i] = game->entities.sprites[j];
                break;
            }
        }
    }
    f32 xs[MAX_ENTITY_COUNT];
    f32 ys[MAX_ENTITY_COUNT];
    Direction dirs[MAX_ENTITY_COUNT];
    for (u32 i = 0; i < entities_to_draw_count; i++) {
        u32 id = entities_to_draw[i];
        for (u32 j = 0; j < game->entities.located_count; j++) {
            if (game->entities.located[j] == id) {
                xs[i] = (f32)game->entities.positions[j].x;
                ys[i] = (f32)game->entities.positions[j].y;
                dirs[i] = game->entities.directions[j];
                break;
            }
        }
    }
    f32 dists[MAX_ENTITY_COUNT];
    for (u32 i = 0; i < entities_to_draw_count; i++) {
        for (u32 j = 0; j < game->entities.moving_count; j++) {
            if (entities_to_draw[i] == game->entities.moving[j]) {
                dists[i] = game->entities.distances[j];
                break;
            }
        }
    }

    for (u32 i = 0; i < entities_to_draw_count; i++) {
        u32 sprite_offset = 0;
        if (ms[i] & ENTITY_MOVING_BIT) {
            f32 animation_frames = 4.0f;
            f32 n = (dists[i] / (1.0f / animation_frames));
            sprite_offset += (u32)n * 4;
            switch (dirs[i]) {
            case NORTH:
            case SOUTH:
                sprite_offset += ((u32)ys[i] % 2) * (4 * 4);
                break;
            case WEST:
            case EAST:
                sprite_offset += ((u32)xs[i] % 2) * (4 * 4);
                break;
            }
        }
        switch (dirs[i]) {
        case NORTH:
            ss[i] = 7721 + sprite_offset;
            break;
        case EAST:
            ss[i] = 7722 + sprite_offset;
            break;
        case SOUTH:
            ss[i] = 7723 + sprite_offset;
            break;
        case WEST:
            ss[i] = 7724 + sprite_offset;
            break;
        }
    }
    for (u32 i = 0; i < entities_to_draw_count; i++) {
        if (ms[i] & ENTITY_MOVING_BIT) {
            V2f v;
            v.x = xs[i];
            v.y = ys[i];
            for (u32 j = 0; j < game->entities.moving_count; j++) {
                if (game->entities.moving[j] == entities_to_draw[i]) {
                    switch (game->entities.directions[j]) {
                    case NORTH:
                        v.y -= game->entities.distances[j];
                        break;
                    case WEST:
                        v.x -= game->entities.distances[j];
                        break;
                    case SOUTH:
                        v.y += game->entities.distances[j];
                        break;
                    case EAST:
                        v.x += game->entities.distances[j];
                        break;
                    }
                    break;
                }
            }
            xs[i] = v.x - game->camera_x + 7.0f;
            ys[i] = v.y - game->camera_y + 5.0f;
        }
    }

    for (u32 i = 0; i < entities_to_draw_count; i++) {
        i32 ts = (i32)tile_size;
        render_queue_push_sprite(render_queue, ss[i], (i32)(xs[i] * ts + ts),
                                 (i32)(ys[i] * ts + ts), ts);
    }
}