{-# LANGUAGE ScopedTypeVariables #-} module Building (buildingSize, buildingType, activateFacingBuilding) where import DB import BuildingData import Data.List import Facing import Data.Maybe import Control.Monad.Maybe import Plane import Position import TerrainData import Control.Monad.Error import NodeData import CharacterAdvancement -- | The total occupied surface area of a building. buildingSize :: (DBReadable db) => BuildingRef -> db Integer buildingSize = liftM (genericLength . buildingOccupies) . buildingType buildingType :: (DBReadable db) => BuildingRef -> db BuildingType buildingType building_ref = do constructed <- liftM extractParent $ dbWhere building_ref case constructed of Just (Constructed _ _ building_type) -> return building_type _ -> error "buildingSize: impossible case" deleteBuilding :: BuildingRef -> DB () deleteBuilding = dbUnsafeDeleteObject (error "deleteBuilding: impossible case, buildings shouldn't contain anything.") -- | Activate the facing building, returns True iff any building was actually activated. activateFacingBuilding :: Facing -> CreatureRef -> DB Bool activateFacingBuilding face creature_ref = liftM (fromMaybe False) $ runMaybeT $ do (plane_ref,position) <- MaybeT $ liftM extractParent $ dbWhere creature_ref buildings <- lift $ whatIsOccupying plane_ref $ offsetPosition (facingToRelative face) position liftM or $ lift $ forM buildings $ \building_ref -> do building_type <- buildingType building_ref activateBuilding building_type creature_ref building_ref activateBuilding :: BuildingType -> CreatureRef -> BuildingRef -> DB Bool activateBuilding (Node n) creature_ref building_ref = do captureNode n creature_ref building_ref return True activateBuilding Portal creature_ref building_ref = do m_creature_position :: Maybe (PlaneRef,Position) <- liftM extractParent $ dbWhere creature_ref m_portal_position :: Maybe (PlaneRef,Position) <- liftM extractParent $ dbWhere building_ref when (fmap fst m_creature_position /= fmap fst m_portal_position) $ throwError $ DBError "activateBuilding: creature and portal on different planes" case (m_creature_position,m_portal_position) of (Just (plane_ref,Position (_,cy)),Just (_,Position (_,py))) -> case () of () | cy < py -> do m_subsequent_loc :: Maybe (Location PlaneRef Subsequent) <- liftM listToMaybe $ dbGetContents plane_ref case m_subsequent_loc of Just loc -> (portalCreatureTo 1 creature_ref $ child loc) >> return True _ -> throwError $ DBErrorFlag NoStargateAddress () | cy > py -> do m_previous_loc :: Maybe Subsequent <- liftM extractParent $ dbWhere plane_ref case m_previous_loc of Just loc -> (portalCreatureTo (-1) creature_ref $ subsequent_to loc) >> return True _ -> throwError $ DBErrorFlag NoStargateAddress () | otherwise -> throwError $ DBErrorFlag BuildingApproachWrongAngle _ -> throwError $ DBError "activateBuilding: can't decode building-creature relative positions" -- | Deposit a creature in front of (-1) or behind (+1) a random portal on the specified plane. Returns -- the dbMove result from the action. portalCreatureTo :: Integer -> CreatureRef -> PlaneRef -> DB (Location CreatureRef (),Location CreatureRef Standing) portalCreatureTo offset creature_ref plane_ref = do portals <- filterM (liftM (== Portal) . buildingType) =<< dbGetContents plane_ref ideal_position <- if null portals then liftM2 (\x y -> Position (x,y)) (getRandomR (-100,100)) (getRandomR (-100,100)) else do portal <- pickM portals m_position <- liftM (fmap (offsetPosition (0,offset)) . extractParent) $ dbWhere portal return $ fromMaybe (Position (0,0)) m_position position <- pickRandomClearSite 1 0 0 ideal_position (not . (`elem` impassable_terrains)) plane_ref dbPushSnapshot $ TeleportEvent creature_ref dbMove (return . toStanding (Standing plane_ref position Here)) creature_ref captureNode :: NodeType -> CreatureRef -> BuildingRef -> DB () captureNode n creature_ref building_ref = do c <- dbGetCreature creature_ref let result = bumpCharacter (nodeEffect n) c dbModCreature (const $ character_new result) creature_ref deleteBuilding building_ref dbPushSnapshot $ BumpEvent { bump_event_creature = creature_ref, bump_event_new_level = newCharacterLevel result, bump_event_new_class = newCharacterClass result }