Chit* LumosChitBag::NewDenizen( const grinliz::Vector2I& pos, int team ) { const ChitContext* context = Context(); IString itemName; switch (Team::Group(team)) { case TEAM_HOUSE: itemName = (random.Bit()) ? ISC::humanFemale : ISC::humanMale; break; case TEAM_GOB: itemName = ISC::gobman; break; case TEAM_KAMAKIRI: itemName = ISC::kamakiri; break; default: GLASSERT(0); break; } Chit* chit = NewChit(); const GameItem& root = ItemDefDB::Instance()->Get(itemName.safe_str()); chit->Add( new RenderComponent(root.ResourceName())); chit->Add( new PathMoveComponent()); const char* altName = 0; if (Team::Group(team) == TEAM_HOUSE) { altName = "human"; } AddItem(root.Name(), chit, context->engine, team, 0, 0, altName); ReserveBank::Instance()->WithdrawDenizen(chit->GetWallet()); chit->GetItem()->GetTraitsMutable()->Roll( random.Rand() ); chit->GetItem()->GetPersonalityMutable()->Roll( random.Rand(), &chit->GetItem()->Traits() ); chit->GetItem()->FullHeal(); IString nameGen = chit->GetItem()->keyValues.GetIString( "nameGen" ); if ( !nameGen.empty() ) { LumosChitBag* chitBag = chit->Context()->chitBag; if ( chitBag ) { chit->GetItem()->SetProperName(chitBag->NameGen(nameGen.c_str(), chit->ID())); } } AIComponent* ai = new AIComponent(); chit->Add( ai ); chit->Add( new HealthComponent()); chit->SetPosition( (float)pos.x+0.5f, 0, (float)pos.y+0.5f ); chit->GetItem()->SetSignificant(GetNewsHistory(), ToWorld2F(pos), NewsEvent::DENIZEN_CREATED, NewsEvent::DENIZEN_KILLED, 0); if (XenoAudio::Instance()) { Vector3F pos3 = ToWorld3F(pos); XenoAudio::Instance()->PlayVariation(ISC::rezWAV, random.Rand(), &pos3); } return chit; }
bool MOBKeyFilter::Accept(Chit* chit) { GameItem* item = chit->GetItem(); if (item) { IString mob = item->keyValues.GetIString(ISC::mob); if (!mob.empty()) { if (value.empty()) return RelationshipFilter::Accept(chit); else if (value == mob) return RelationshipFilter::Accept(chit); } } return false; }
Chit* LumosChitBag::NewLawnOrnament(const Vector2I& pos, const char* name, int team) { const ChitContext* context = Context(); Chit* chit = NewChit(); GameItem* rootItem = ItemDefDB::Instance()->Get(name).Clone(); // Hack...how to do this better?? if (rootItem->IResourceName() == "ruins1.0") { CStr<32> str; str.Format("ruins1.%d", random.Rand(2)); rootItem->SetResource(str.c_str()); } int size = 1; rootItem->keyValues.Get(ISC::size, &size); MapSpatialComponent* msc = new MapSpatialComponent(); msc->SetBuilding(size, false, 0); msc->SetBlocks((rootItem->flags & GameItem::PATH_NON_BLOCKING) ? false : true); chit->Add(msc); MapSpatialComponent::SetMapPosition(chit, pos.x, pos.y); chit->Add(new RenderComponent(rootItem->ResourceName())); chit->Add(new HealthComponent()); AddItem(rootItem, chit, context->engine, team, 0); IString proc = rootItem->keyValues.GetIString("procedural"); if (!proc.empty()) { ProcRenderInfo info; AssignProcedural(chit->GetItem(), &info); chit->GetRenderComponent()->SetProcedural(0, info); } context->engine->particleSystem->EmitPD(ISC::constructiondone, ToWorld3F(pos), V3F_UP, 0); if (XenoAudio::Instance()) { Vector3F pos3 = ToWorld3F(pos); XenoAudio::Instance()->PlayVariation(ISC::rezWAV, random.Rand(), &pos3); } return chit; }
void CoreScript::DoTickNeutral( int delta, int nSpawnTicks ) { int lesser, greater, denizen; const Census& census = Context()->chitBag->census; census.NumByType(&lesser, &greater, &denizen); IString defaultSpawn = Context()->worldMap->GetSectorData(sector).defaultSpawn; int typical = 0; int numOf = census.NumOf(defaultSpawn, &typical); bool lesserPossible = (lesser < TYPICAL_LESSER) && (!typical || numOf < typical * 2); Vector2I pos2i = ToWorld2I(parentChit->Position()); Vector2I sector = ToSector(pos2i); if (nSpawnTicks && lesserPossible) { #if SPAWN_MOBS > 0 int spawnEnabled = Context()->chitBag->GetSim()->SpawnEnabled() & Sim::SPAWN_LESSER; if (Context()->chitBag->GetSim() && spawnEnabled && !defaultSpawn.empty()) { Vector3F pf = { (float)pos2i.x + 0.5f, 0, (float)pos2i.y + 0.5f }; int nSpawn = (defaultSpawn == ISC::trilobyte) ? 4 : 1; int team = Team::GetTeam(defaultSpawn); GLASSERT(team != TEAM_NEUTRAL); for (int i = 0; i < nSpawn; ++i) { Context()->chitBag->NewMonsterChit(pf, defaultSpawn.safe_str(), team); pf.x += 0.05f; } } #endif } // Clear the work queue - chit is gone that controls this. if (!workQueue || workQueue->HasJob()) { delete workQueue; workQueue = new WorkQueue(); workQueue->InitSector(parentChit, ToSector(parentChit->Position())); } }
void NewsEvent::Console(GLString* str, ChitBag* chitBag, int shortNameID) const { *str = ""; Vector2I sector = ToSector(ToWorld2I(pos)); const GameItem* first = ItemDB::Instance()->Active(firstItemID); // const GameItem* second = ItemDB::Instance()->Active(secondItemID); IString firstName = IDToName(firstItemID, firstItemID == shortNameID); IString secondName = IDToName(secondItemID, secondItemID == shortNameID); if (firstName.empty()) firstName = StringPool::Intern("[unknown]"); if (secondName.empty()) secondName = StringPool::Intern("[unknown]"); float age = float(double(date) / double(AGE_IN_MSEC)); IString domain; if (chitBag->Context()->worldMap) { const SectorData& sd = chitBag->Context()->worldMap->GetSectorData(sector); domain = sd.name; } IString firstTeamName = Team::IsCoreController(firstTeam) ? Team::Instance()->TeamName(firstTeam) : IString(); IString secondTeamName = Team::IsCoreController(secondTeam) ? Team::Instance()->TeamName(secondTeam) : IString(); switch (what) { case DENIZEN_CREATED: str->Format("%.2f: Denizen %s " MOB_created " at %s with %s.", age, firstName.c_str(), domain.safe_str(), firstTeamName.safe_str()); break; case DENIZEN_KILLED: str->Format("%.2f: Denizen %s (%s) " MOB_destroyed " at %s by %s.", age, firstName.safe_str(), Team::IsRogue(firstTeam) ? "rogue" : firstTeamName.safe_str(), domain.safe_str(), secondName.safe_str()); break; case GREATER_MOB_CREATED: // They get created at the center, then sent. So the domain is meaningless. str->Format("%.2f: %s " MOB_created ".", age, firstName.safe_str()); break; case DOMAIN_CREATED: // "taken over" is interesting; a domain getting created is not. *str = ""; break; case ROGUE_DENIZEN_JOINS_TEAM: str->Format("%.2f: Rogue Denizen %s joins at %s with %s.", age, firstName.safe_str(), domain.safe_str(), firstTeamName.safe_str() ); break; case GREATER_MOB_KILLED: str->Format("%.2f: %s " MOB_destroyed " at %s by %s.", age, firstName.safe_str(), domain.safe_str(), secondName.safe_str()); break; case DOMAIN_DESTROYED: GLASSERT(firstTeam); // how is a neutral destroyed?? str->Format("%.2f: %s domain %s " MOB_destroyed " by %s.", age, firstTeamName.safe_str(), domain.safe_str(), secondName.safe_str()); break; // Neutral domains are taken over. // Subdomains are conquored. case DOMAIN_TAKEOVER: str->Format("%.2f: %s occupied by %s.", age, domain.safe_str(), firstTeamName.safe_str()); break; case DOMAIN_CONQUER: str->Format("%.2f: %s is conquered by %s.", age, domain.safe_str(), firstTeamName.safe_str() ); break; case SUPERTEAM_DELETED: GLASSERT(firstTeam); str->Format("%.2f: %s super domain %s " MOB_destroyed ". Sub-domains are now self controlled.", age, firstTeamName.safe_str(), domain.safe_str()); break; case SUBTEAM_DELETED: GLASSERT(firstTeam); GLASSERT(secondTeam); str->Format("%.2f: %s no longer controlled by %s.", age, firstTeamName.safe_str(), secondTeamName.safe_str()); break; case FORGED: str->Format("%.2f: %s forged %s at %s.", age, secondName.safe_str(), firstName.c_str(), domain.safe_str()); break; case UN_FORGED: str->Format("%.2f: %s " MOB_destroyed " %s at %s.", age, secondName.c_str(), firstName.c_str(), domain.c_str()); break; case PURCHASED: if (first) { str->Format("%.2f: %s purchased %s at %s for %d (%d tax).", age, secondName.c_str(), firstName.c_str(), domain.c_str(), first->GetValue(), int(first->GetValue() * SALES_TAX)); } else { str->Format("%.2f: %s purchased %s at %s.", age, secondName.c_str(), firstName.c_str(), domain.c_str()); } break; case STARVATION: str->Format("%.2f: %s has been overcome by starvation at %s.", age, firstName.c_str(), domain.c_str()); break; case BLOOD_RAGE: str->Format("%.2f: a distraught %s is overcome by blood rage at %s.", age, firstName.c_str(), domain.c_str()); break; case VISION_QUEST: str->Format("%.2f: %s is consumed by despair at %s and leaves for a vision quest.", age, firstName.c_str(), domain.c_str()); break; case GREATER_SUMMON_TECH: str->Format("%.2f: %s is called to %s by the siren song of Tech.", age, firstName.c_str(), domain.c_str()); break; case ATTITUDE_FRIEND: str->Format("%.2f: %s sees %s with friendly intent.", age, firstName.safe_str(), secondName.safe_str()); break; case ATTITUDE_NEUTRAL: str->Format("%.2f: %s sees %s with neutral regard.", age, firstName.safe_str(), secondName.safe_str()); break; case ATTITUDE_ENEMY: str->Format("%.2f: %s sees %s as an enemy.", age, firstName.safe_str(), secondName.safe_str()); break; default: GLASSERT(0); } }
Chit* LumosChitBag::NewBuilding(const Vector2I& pos, const char* name, int team) { const ChitContext* context = Context(); Chit* chit = NewChit(); const GameItem& rootItem = ItemDefDB::Instance()->Get(name); GameItem* item = rootItem.Clone(); // Hack...how to do this better?? if (item->IResourceName() == "pyramid0") { CStr<32> str; str.Format("pyramid%d", random.Rand(3)); item->SetResource(str.c_str()); } if (item->IResourceName() == ISC::kiosk) { switch (random.Rand(4)) { case 0: item->SetResource("kiosk.n"); break; case 1: item->SetResource("kiosk.m"); break; case 2: item->SetResource("kiosk.s"); break; default:item->SetResource("kiosk.c"); break; } } int size = 1; rootItem.keyValues.Get(ISC::size, &size); int porch = 0; rootItem.keyValues.Get(ISC::porch, &porch); const int circuit = 0; // Should be pre-cleared. But recover from weird situations by clearing. // Note that water is a real problem. Rectangle2I r; r.Set(pos.x, pos.y, pos.x + size - 1, pos.y + size - 1); for (Rectangle2IIterator it(r); !it.Done(); it.Next()) { const WorldGrid& wg = context->worldMap->GetWorldGrid(it.Pos()); GLASSERT(wg.IsLand()); (void)wg; context->worldMap->SetRock(it.Pos().x, it.Pos().y, 0, false, 0); context->worldMap->SetPlant(it.Pos().x, it.Pos().y, 0, 0); } MapSpatialComponent* msc = new MapSpatialComponent(); msc->SetBuilding(size, porch != 0, circuit); msc->SetBlocks((rootItem.flags & GameItem::PATH_NON_BLOCKING) ? false : true); chit->Add(msc); MapSpatialComponent::SetMapPosition(chit, pos.x, pos.y); chit->Add(new RenderComponent(item->ResourceName())); chit->Add(new HealthComponent()); AddItem(item, chit, context->engine, team, 0); IString script = rootItem.keyValues.GetIString("script"); if (!script.empty()) { Component* s = ComponentFactory::Factory(script.c_str(), &chitContext); GLASSERT(s); chit->Add(s); } IString proc = rootItem.keyValues.GetIString("procedural"); if (!proc.empty()) { ProcRenderInfo info; AssignProcedural(chit->GetItem(), &info); chit->GetRenderComponent()->SetProcedural(0, info); } IString consumes = rootItem.keyValues.GetIString(ISC::zone); if (!consumes.empty()) { Component* s = ComponentFactory::Factory("EvalBuildingScript", &chitContext); GLASSERT(s); chit->Add(s); } IString nameGen = rootItem.keyValues.GetIString( "nameGen"); if ( !nameGen.empty() ) { IString p = Context()->chitBag->NameGen(nameGen.c_str(), chit->random.Rand()); chit->GetItem()->SetProperName( p ); } #if 0 // debugging SectorPort sp; sp.sector.Set( pos.x/SECTOR_SIZE, pos.y/SECTOR_SIZE ); sp.port = 1; worldMap->SetRandomPort( sp ); #endif context->engine->particleSystem->EmitPD( ISC::constructiondone, ToWorld3F( pos ), V3F_UP, 0 ); if (XenoAudio::Instance()) { Vector3F pos3 = ToWorld3F(pos); XenoAudio::Instance()->PlayVariation(ISC::rezWAV, random.Rand(), &pos3); } return chit; }
double EvalBuildingScript::EvalIndustrial( bool debugLog ) { Chit* building = ParentChit(); int hitB = 0, hitIBuilding = 0, hitNBuilding = 0, hitWater = 0, hitPlant = 0, hitRock = 0, hitWaterfall = 0; int hitShrub = 0; // doesn't terminate a ray. if (lastEval == 0 || (Context()->chitBag->AbsTime() - lastEval) > 2000) { lastEval = Context()->chitBag->AbsTime(); GameItem* item = building->GetItem(); GLASSERT(item); reachable = true; IString consume = item->keyValues.GetIString(ISC::zone); if (consume.empty()) { eval = 0; return eval; } MapSpatialComponent* msc = GET_SUB_COMPONENT(building, SpatialComponent, MapSpatialComponent); GLASSERT(msc); if (!msc) { eval = 0; return eval; } Rectangle2I porch = msc->PorchPos(); static const int RAD = 4; Rectangle2I bounds = porch; if (porch.min.x == 0) { // shouldn't happen, but be sure. GLASSERT(0); eval = 0; return eval; } bounds.Outset(RAD); Vector2I sector = ToSector(building->Position()); WorldMap* worldMap = Context()->worldMap; Rectangle2I mapBounds = worldMap->Bounds(); if (!mapBounds.Contains(bounds)) { eval = 0; return eval; // not worth dealing with edge of world } // Check if we can go from the core to the porch. // And make sure the core is inUse! CoreScript* cs = CoreScript::GetCore(ToSector(porch.min)); if (!cs || !cs->InUse()) reachable = false; if (reachable) { const SectorData& sd = worldMap->GetSectorData(ToSector(porch.min)); reachable = worldMap->CalcPath(ToWorld2F(sd.core), ToWorld2F(porch.min), 0, 0, false); } CChitArray arr; BuildingFilter buildingFilter; const FluidSim* fluidSim = Context()->physicsSims->GetFluidSim(sector); bool hasWaterfalls = fluidSim->NumWaterfalls() > 0; LumosChitBag* chitBag = Context()->chitBag; Rectangle2IEdgeIterator it(bounds); while (!it.Done()) { Vector2I pos = { it.Pos().x >= porch.max.x ? porch.max.x : porch.min.x, it.Pos().y >= porch.max.y ? porch.max.y : porch.min.y }; LineWalk walk(pos.x, pos.y, it.Pos().x, it.Pos().y); walk.Step(); // ignore where we are standing. while ( !walk.Done() ) { // non-intuitive iterator. See linewalk docs. // - building // - plant // - ice // - rock // - waterfall // - water // Buildings. Can be 2x2. Extend out beyond current check. bool hitBuilding = false; Vector2I p = walk.P(); // Don't count self as a hit, but stops the ray cast. // Also, use a larger radius because buildings can be 2x2 chitBag->QuerySpatialHash(&arr, ToWorld2F(p), 0.8f, 0, &buildingFilter); for (int i = 0; i < arr.Size(); ++i) { if (arr[i] != building) { MapSpatialComponent* buildingMSC = GET_SUB_COMPONENT(arr[i], SpatialComponent, MapSpatialComponent); GLASSERT(buildingMSC); if (buildingMSC->Bounds().Contains(p)) { hitBuilding = true; double thisSys = arr[i]->GetItem()->GetBuildingIndustrial(); hitB++; if (thisSys <= -0.5) hitNBuilding++; if (thisSys >= 0.5) hitIBuilding++; break; } } } if (hitBuilding) break; const WorldGrid& wg = worldMap->GetWorldGrid(p.x, p.y); if (wg.Plant()) { // int type = wg.Plant() - 1; int stage = wg.PlantStage(); if (stage >= 2) { ++hitPlant; break; } else { hitShrub++; } } if (wg.RockHeight()) { ++hitRock; break; } if (wg.IsWater()) { ++hitWater; break; } Rectangle2I wb; wb.min = wb.max = p; if (hasWaterfalls && fluidSim->ContainsWaterfalls(wb)) { ++hitWaterfall; break; } walk.Step(); } it.Next(); } // Note rock/ice isn't counted either way. int natural = hitNBuilding + hitWater + hitPlant + 10 * hitWaterfall + hitShrub / 4; // small plants don't add to rRays, so divide is okay. int industrial = hitIBuilding; int nRays = hitNBuilding + hitWater + hitPlant + hitWaterfall + hitIBuilding; eval = 0; if (nRays) { // With this system, that one ray (say from a distillery to plant) can be // hugely impactful. This may need tweaking: if (nRays < 2) nRays = 2; eval = double(industrial - natural) / double(nRays); } eval = Clamp(eval, -1.0, 1.0); if (debugLog) { Vector2I pos = ToWorld2I(building->Position()); GLOUTPUT(("Building %s at %d,%d eval=%.2f nRays=%d \n hit: Build=%d (I=%d N=%d) water=%d plant=%d rock=%d\n", building->GetItem()->Name(), pos.x, pos.y, eval, nRays, hitB, hitIBuilding, hitNBuilding, hitWater, hitPlant, hitRock)); (void)pos; } } return eval; }