OrderedTaskPoint*
AbstractTaskFactory::createPoint(const LegalPointType_t type,
                                 const Waypoint &wp,
                                 const fixed _start_radius,
                                 const fixed _turnpoint_radius,
                                 const fixed _finish_radius) const
{
  fixed start_radius = _start_radius;
  fixed turnpoint_radius = _turnpoint_radius;
  fixed finish_radius = _finish_radius;

  getPointDefaultSizes(type, start_radius, turnpoint_radius, finish_radius);

  switch (type) {
  case START_SECTOR:
    return createStart(new FAISectorZone(wp.location, false), wp);
  case START_LINE:
    return createStart(new LineSectorZone(wp.location, start_radius), wp);
  case START_CYLINDER:
    return createStart(new CylinderZone(wp.location, start_radius), wp);
  case START_BGA:
    return createStart(new BGAStartSectorZone(wp.location), wp);
  case FAI_SECTOR:
    return createAST(new FAISectorZone(wp.location, true), wp);
  case KEYHOLE_SECTOR:
    return createAST(new KeyholeZone(wp.location), wp);
  case BGAFIXEDCOURSE_SECTOR:
    return createAST(new BGAFixedCourseZone(wp.location), wp);
  case BGAENHANCEDOPTION_SECTOR:
    return createAST(new BGAEnhancedOptionZone(wp.location), wp);
  case AST_CYLINDER:
    return createAST(new CylinderZone(wp.location, turnpoint_radius), wp);
  case AAT_CYLINDER:
    return createAAT(new CylinderZone(wp.location, turnpoint_radius), wp);
  case AAT_SEGMENT:
    return createAAT(new SectorZone(wp.location, turnpoint_radius), wp);
  case AAT_ANNULAR_SECTOR:
    return createAAT(new AnnularSectorZone(wp.location, turnpoint_radius), wp);
  case FINISH_SECTOR:
    return createFinish(new FAISectorZone(wp.location, false), wp);
  case FINISH_LINE:
    return createFinish(new LineSectorZone(wp.location, finish_radius), wp);
  case FINISH_CYLINDER:
    return createFinish(new CylinderZone(wp.location, finish_radius), wp);
  }

  assert(1);
  return NULL;
}
bool 
AbstractTaskFactory::remove(const unsigned position, 
                            const bool auto_mutate)
{
  if (position >= m_task.TaskSize())
    return false;

  if (auto_mutate) {
    if (position == 0) {
      // special case, remove start point..
      if (m_task.TaskSize() == 1) {
        return m_task.remove(0);
      } else {
        // create new start point from next point
        StartPoint* sp = createStart(m_task.getTaskPoint(1)->GetWaypoint());
        bool success = m_task.remove(0) && m_task.replace(*sp, 0);
        delete sp;
        return success;
      }
    } else if (is_position_finish(position - 1) && (position + 1 == m_task.TaskSize())) {
      // create new finish from previous point
      FinishPoint* sp = createFinish(
          m_task.getTaskPoint(position - 1)->GetWaypoint());
      bool success = m_task.remove(position) &&
        m_task.replace(*sp, position - 1);
      delete sp;
      return success;
    } else {
      // intermediate point deleted, nothing special to do
      return m_task.remove(position);
    }
  }

  return m_task.remove(position);
}
bool 
AbstractTaskFactory::replace(const OrderedTaskPoint &new_tp,
                             const unsigned position,
                             const bool auto_mutate)
{
  if (auto_mutate) {
    if (validType(new_tp, position))
      // ok to replace directly
      return m_task.replace(new_tp, position);

    // will need to convert type of candidate
    OrderedTaskPoint *tp;
    if (position == 0) {
      // candidate must be transformed into a startpoint
      tp = createStart(new_tp.GetWaypoint());
    } else if (is_position_finish(position) &&
               position + 1 == m_task.TaskSize()) {
      // this point must be mutated into a finish
      tp = createFinish(new_tp.GetWaypoint());
    } else {
      // this point must be mutated into an intermediate
      tp = createIntermediate(new_tp.GetWaypoint());
    }

    bool success = m_task.replace(*tp, position);
    delete tp;
    return success;
  }

  return m_task.replace(new_tp, position);
}
bool
AbstractTaskFactory::mutate_closed_finish_per_task_type()
{
  if (m_task.TaskSize() < 2)
    return false;

  if (!is_position_finish(m_task.TaskSize() - 1))
    return false;

  bool changed = false;

  if (get_ordered_task_behaviour().is_closed) {
    if (!is_closed()) {
      OrderedTaskPoint *tp = m_task.get_tp(m_task.TaskSize() - 1);
      assert(tp);
      if (tp->GetType() == TaskPoint::FINISH) {
        FinishPoint *fp = createFinish(m_task.get_tp(0)->GetWaypoint());
        assert(fp);
        remove(m_task.TaskSize() - 1, false);
        append(*fp, false);
        delete fp;
        changed = true;
      }
    }
  }
  return changed;
}
FinishPoint* 
AbstractTaskFactory::createFinish(const Waypoint &wp) const
{
  LegalPointType_t type = m_behaviour.sector_defaults.finish_type;
  if (!validFinishType(type))
    type = *m_finish_types.begin();

  return createFinish(type, wp);
}
bool
AbstractTaskFactory::CheckAddFinish()
{
 if (m_task.TaskSize() < 2)
   return false;

 if (m_task.has_finish())
   return false;

 FinishPoint *fp = createFinish(m_task.get_tp(m_task.TaskSize() - 1)->GetWaypoint());
 assert(fp);
 remove(m_task.TaskSize() - 1, false);
 append(*fp, false);
 delete fp;

 return true;
}
bool 
AbstractTaskFactory::append(const OrderedTaskPoint &new_tp,
                            const bool auto_mutate)
{
  if (m_task.is_max_size())
    return false;

  if (auto_mutate) {
    if (!m_task.TaskSize()) {
      // empty task, so add as a start point
      if (validType(new_tp, m_task.TaskSize())) {
        // candidate is ok, so add it
        return m_task.append(new_tp);
      } else {
        // candidate must be transformed into a startpoint
        StartPoint* sp = createStart(new_tp.GetWaypoint());
        bool success = m_task.append(*sp);
        delete sp;
        return success;
      }
    }

    // non-empty task

    if (m_task.has_finish()) {
      // old finish must be mutated into an intermediate point
      IntermediateTaskPoint* sp = createIntermediate(m_task.getTaskPoint(
          m_task.TaskSize() - 1)->GetWaypoint());

      m_task.replace(*sp, m_task.TaskSize()-1);
      delete sp;
    }

    if (validType(new_tp, m_task.TaskSize()))
      // ok to append directly
      return m_task.append(new_tp);

    // this point must be mutated into a finish
    FinishPoint* sp = createFinish(new_tp.GetWaypoint());
    bool success = m_task.append(*sp);
    delete sp;
    return success;
  }

  return m_task.append(new_tp);
}
bool Game::update() {
	frect tempRec;
	char buffer[256];
	if(curState != gplay) {
		if(curState != cselect) {
			menu.update();
			menu.render();
			tempInfo.type = screenSprite;
			tempInfo.asset = &backGround;
			D3DXMatrixIdentity(&tempInfo.matrix);
			if(curState != finish) {
				D3DXMatrixTranslation(&tempInfo.matrix,Engine::instance()->width()/2,Engine::instance()->height()/4,0);
			} else {
				D3DXMatrixTranslation(&tempInfo.matrix,Engine::instance()->width()/2,Engine::instance()->height()/2,0);
			}
			Engine::instance()->addRender(tempInfo);
			if(curState == finish) {
				tempInfo.type = text;
				tempRec.top = 0.1f;
				tempRec.bottom = 0.2f;
				tempRec.left = 0.0f;
				tempRec.right = 1.0f;
				if(p1Won) {
					sprintf(buffer,"%s WINS!",charList[p1Select].name.c_str());
					p1Name.text = buffer;
					p1Name.rect = tempRec;
					tempInfo.asset = &p1Name;
				} else {
					sprintf(buffer,"%s WINS!",charList[p2Select].name.c_str());
					p2Name.text = buffer;
					p2Name.rect = tempRec;
					tempInfo.asset = &p2Name;
				}
				Engine::instance()->addRender(tempInfo);
			}

			if(Engine::instance()->getMessage("CharSelect")) {
				menu.resetSelection();
				p1Lock = false;
				p2Lock = false;
				p1Select = 0;
				p2Select = 0;
				startDelay = 3.0f;
				curState = cselect;
			}
			if(Engine::instance()->getMessage("MainMenu")) {
				menu.resetSelection();
				createMenu();
			}
			if(Engine::instance()->getMessage("Options")) {
				menu.resetSelection();
				createOptions();
			}
			if(Engine::instance()->getMessage("Help")) {
				menu.resetSelection();
				createHelp();
			}
			if(Engine::instance()->getMessage("Quit")) {
				play = false;
			}
			if(Engine::instance()->getMessage("ToggleRensa")) {
				_rensa = !_rensa;
				createOptions();
			}
			if(Engine::instance()->getMessage("ToggleMagic")) {
				_magic = !_magic;
				createOptions();
			}
			if(Engine::instance()->getMessage("replay")) {
				startTetris();
			}
			//if (Engine::instance()->getMessage("BindControllers")) {
			//	thought about it, and this is actually not necessary since it's only two controllers that are loaded kinda. fuckit man. fuckit.
			//}
		} else {
			//character select menu
			if(!p1Lock) {
				if (Engine::instance()->getFlags("SelectionUp")&buttonFlags::_repeat || Engine::instance()->getFlags("SelectionUpDpad")&buttonFlags::_repeat) {
					--p1Select;
					Engine::instance()->playSound(rollover, soundvec, soundvec);
				}
				else if (Engine::instance()->getFlags("SelectionDown")&buttonFlags::_repeat || Engine::instance()->getFlags("SelectionDownDpad")&buttonFlags::_repeat) {
					++p1Select;
					Engine::instance()->playSound(rollover, soundvec, soundvec);
				} else if(Engine::instance()->getFlags("Accept")&buttonFlags::_pushed) {
					p1Lock = true;
					Engine::instance()->playSound(click, soundvec, soundvec);
				}

				if(p1Select < 0) {
					p1Select = charList.size()-1;
				} else if(p1Select >= charList.size()) {
					p1Select = 0;
				}
			}

			if(!p2Lock) {
				if (Engine::instance()->getFlags("SelectionUp2")&buttonFlags::_repeat || Engine::instance()->getFlags("SelectionUp2Dpad")&buttonFlags::_repeat) {
					--p2Select;
					Engine::instance()->playSound(rollover, soundvec, soundvec);
				}
				else if (Engine::instance()->getFlags("SelectionDown2")&buttonFlags::_repeat || Engine::instance()->getFlags("SelectionDown2Dpad")&buttonFlags::_repeat) {
					++p2Select;
					Engine::instance()->playSound(rollover, soundvec, soundvec);
				} else if(Engine::instance()->getFlags("Accept2")&buttonFlags::_pushed) {
					p2Lock = true;
					Engine::instance()->playSound(click, soundvec, soundvec);
				}

				if(p2Select < 0) {
					p2Select = charList.size() - 1;
				} else if(p2Select >= charList.size()) {
					p2Select = 0;
				}
			}

			//draw character select
			tempInfo.type = text;

			if(p1Lock) {
				tempRec.top = 0.1f;
				tempRec.bottom = 0.2f;
				tempRec.left = 0.0f;
				tempRec.right = 0.5f;
				p1Name.text = charList[p1Select].name;
			} else {
				tempRec.top = (p1Select*(1.0f/charList.size()));
				tempRec.bottom = tempRec.top + 0.1;
				tempRec.left = 0.4f;
				tempRec.right = 0.6f;
				p1Name.text = "P1";
			}
			p1Name.rect = tempRec;
			tempInfo.asset = &p1Name;
			Engine::instance()->addRender(tempInfo);
			//p1 magic
			tempRec.left = 0.0f;
			tempRec.right = 0.5f;

			for(int i = 0; i < 4; ++i) {
				tempRec.top = 0.6f+(i*0.1f);
				tempRec.bottom = 0.7f+(i*0.1f);
				p1Power[i].text = charList[p1Select].aName[i];
				p1Power[i].rect = tempRec;
				tempInfo.asset = &p1Power[i];
				Engine::instance()->addRender(tempInfo);
			}

			if(p2Lock) {
				tempRec.top = 0.1f;
				tempRec.bottom = 0.2f;
				tempRec.left = 0.5f;
				tempRec.right = 1.0f;
				p2Name.text = charList[p2Select].name;
			} else {
				tempRec.top = 0.2+(p2Select*(1.0f/charList.size()));
				tempRec.bottom = tempRec.top + 0.1;
				tempRec.left = 0.4f;
				tempRec.right = 0.6f;
				p2Name.text = "P2";
			}
			p2Name.rect = tempRec;
			tempInfo.asset = &p2Name;
			Engine::instance()->addRender(tempInfo);

			//p2 magic
			tempRec.left = 0.5f;
			tempRec.right = 1.0f;

			for(int i = 0; i < 4; ++i) {
				tempRec.top = 0.6f+(i*0.1f);
				tempRec.bottom = 0.7f+(i*0.1f);
				p2Power[i].text = charList[p2Select].aName[i];
				p2Power[i].rect = tempRec;
				tempInfo.asset = &p2Power[i];
				Engine::instance()->addRender(tempInfo);
			}

			tempInfo.type = screenSprite;
			for(int i = 0; i < charList.size(); ++i) {
				tempInfo.asset = &charList[i].icon;
				D3DXMatrixIdentity(&tempInfo.matrix);
				D3DXMatrixIdentity(&charTrans);
				D3DXMatrixScaling(&tempInfo.matrix,((float)(Engine::instance()->height()/charList.size()))/charList[i].icon.image->texInfo.Width,((float)(Engine::instance()->height()/charList.size()))/charList[i].icon.image->texInfo.Height,0);
				D3DXMatrixTranslation(&charTrans,Engine::instance()->width()/2.0f,((Engine::instance()->height()/charList.size())/2.0f)+(i*(Engine::instance()->height()/charList.size())),0);
				D3DXMatrixMultiply(&tempInfo.matrix,&tempInfo.matrix,&charTrans);
				Engine::instance()->addRender(tempInfo);
			}
			if(p1Lock&&p2Lock) {
				startDelay -= Engine::instance()->dt();
				if(startDelay <= 0) {
					//start game
					startTetris();
				}
			}

		}
	}
	else { // GPLAY!
		tetris.Update();
	//draw background
		tempInfo.type = screenSprite;
		tempInfo.asset = &charList[p2Select].selectBackground;
		D3DXMatrixIdentity(&tempInfo.matrix);
		D3DXMatrixIdentity(&charTrans);
		D3DXMatrixTranslation(&charTrans,Engine::instance()->width()/2.0f,Engine::instance()->height()/2.0f,0);
		D3DXMatrixScaling(&tempInfo.matrix,(Engine::instance()->width()*1.0f)/(charList[p2Select].selectBackground.rec.right*1.0f),(Engine::instance()->height()*1.0f)/(charList[p2Select].selectBackground.rec.bottom*1.0f),1.0f);
		D3DXMatrixMultiply(&tempInfo.matrix,&tempInfo.matrix,&charTrans);
		Engine::instance()->addRender(tempInfo);

		tetris.Draw(); // SAD AUST IT CRASHES BECAUSE OF THIS I'M GOING TO BED WTF IS WRONG AAAAAAAAAAAAHHHHHHHHHHHHHHHHHHH
						// CTRL+F FOR: dammitrender
						// to get to my other comments showing where things are.
						// never mind these comments
		
		if(Engine::instance()->getMessage("P1Wins")) {
			p1Won = true;
			createFinish();
			backGround = charList[p1Select].victoryBackground;
			Engine::instance()->playSound(youwin, soundvec, soundvec);
		} else if(Engine::instance()->getMessage("P2Wins")) {
			p1Won = false;
			createFinish();
			backGround = charList[p2Select].victoryBackground;
			Engine::instance()->playSound(youwin, soundvec, soundvec);
		}
	}

	if(showFPS) {
		fps = 1/Engine::instance()->dt();
		sprintf(buffer,"%d",(int)fps);
		fpsText.text = buffer;
		tempInfo.type = text;
		tempInfo.asset = &fpsText;
		Engine::instance()->addRender(tempInfo);
	}

	Engine::instance()->render();
	Engine::instance()->clearMessages();
	return play;
}