/*QUAKED target_script_trigger (1 .7 .2) (-8 -8 -8) (8 8 8) must have an aiName must have a target when used it will fire its targets */ void target_script_trigger_use(gentity_t *ent, gentity_t *other, gentity_t *activator) { // START Mad Doctor I changes, 8/16/2002 qboolean found = qfalse; // for all entities/bots with this ainame gentity_t *trent = NULL; // Are we using ainame to find another ent instead of using scriptname for this one? if (ent->aiName) { // Find the first entity with this name trent = G_Find(trent, FOFS(scriptName), ent->aiName); // Was there one? if (trent) { // We found it found = qtrue; // Play the script G_Script_ScriptEvent(trent, "trigger", ent->target); } // if (trent)... } // if (ent->aiName)... // Use the old method if we didn't find an entity with the ainame if (!found) { if (ent->scriptName) { G_Script_ScriptEvent(ent, "trigger", ent->target); } } G_UseTargets(ent, other); }
/* * The ugly as hell coop spawnpoint fixup function. * While coop was planed by id, it wasn't part of * the initial release and added later with patch * to version 2.00. The spawnpoints in some maps * were SNAFU, some have wrong targets and some * no name at all. Fix this by matching the coop * spawnpoint target names to the nearest named * single player spot. */ void SP_FixCoopSpots(edict_t *self) { edict_t *spot; vec3_t d; if (!self) { return; } spot = NULL; while (1) { spot = G_Find(spot, FOFS(classname), "info_player_start"); if (!spot) { return; } if (!spot->targetname) { continue; } VectorSubtract(self->s.origin, spot->s.origin, d); if (VectorLength(d) < 550) { if ((!self->targetname) || (Q_stricmp(self->targetname, spot->targetname) != 0)) { self->targetname = spot->targetname; } return; } } }
//--------------------------------------------------------- void WP_FireDetPack( gentity_t *ent, qboolean alt_fire ) //--------------------------------------------------------- { if ( !ent || !ent->client ) { return; } if ( alt_fire ) { if ( ent->client->ps.eFlags & EF_PLANTED_CHARGE ) { gentity_t *found = NULL; // loop through all ents and blow the crap out of them! while (( found = G_Find( found, FOFS( classname ), "detpack" )) != NULL ) { if ( found->activator == ent ) { VectorCopy( found->currentOrigin, found->s.origin ); found->e_ThinkFunc = thinkF_WP_Explode; found->nextthink = level.time + 100 + random() * 100; G_Sound( found, G_SoundIndex( "sound/weapons/detpack/warning.wav" )); // would be nice if this actually worked? AddSoundEvent( NULL, found->currentOrigin, found->splashRadius*2, AEL_DANGER, qfalse, qtrue );//FIXME: are we on ground or not? AddSightEvent( NULL, found->currentOrigin, found->splashRadius*2, AEL_DISCOVERED, 100 ); } } ent->client->ps.eFlags &= ~EF_PLANTED_CHARGE; } } else { WP_DropDetPack( ent, muzzle, forwardVec ); ent->client->ps.eFlags |= EF_PLANTED_CHARGE; } }
/* ================ G_SelectHumanSpawnPoint go to a random point that doesn't telefrag ================ */ gentity_t *G_SelectHumanSpawnPoint( vec3_t preference ) { gentity_t *spot; int count; gentity_t *spots[ MAX_SPAWN_POINTS ]; if( level.numHumanSpawns <= 0 ) return NULL; count = 0; spot = NULL; while( ( spot = G_Find( spot, FOFS( classname ), BG_Buildable( BA_H_SPAWN )->entityName ) ) != NULL ) { if( !spot->spawned ) continue; if( spot->health <= 0 ) continue; if( !spot->s.groundEntityNum ) continue; if( spot->clientSpawnTime > 0 ) continue; if( G_CheckSpawnPoint( spot->s.number, spot->s.origin, spot->s.origin2, BA_H_SPAWN, NULL ) != NULL ) continue; spots[ count ] = spot; count++; } if( !count ) return NULL; return G_ClosestEnt( preference, spots, count ); }
void GetNextTrack( gentity_t *ent ) { gentity_t *track = NULL; gentity_t *next; gentity_t *choice[MAXCHOICES]; int num_choices = 0; int rval; next = ent->nextTrain; if ( !( next->track ) ) { G_Printf( "NULL track name for %s on %s\n", ent->classname, next->targetname ); return; } while ( 1 ) { track = G_Find( track, FOFS( targetname ), next->track ); if ( !track ) { break; } choice[num_choices++] = track; if ( num_choices == MAXCHOICES ) { break; } } if ( !num_choices ) { G_Printf( "GetNextTrack didnt find a track\n" ); return; } rval = rand() % num_choices; ent->nextTrain = NULL; ent->target = choice[rval]->targetname; }
void misc_weapon_shooter_aim( gentity_t *self ) { //update my aim if ( self->target ) { gentity_t *targ = G_Find( NULL, FOFS(targetname), self->target ); if ( targ ) { self->enemy = targ; VectorSubtract( targ->currentOrigin, self->currentOrigin, self->client->renderInfo.muzzleDir ); VectorCopy( targ->currentOrigin, self->pos1 ); vectoangles( self->client->renderInfo.muzzleDir, self->client->ps.viewangles ); SetClientViewAngle( self, self->client->ps.viewangles ); //FIXME: don't keep doing this unless target is a moving target? self->nextthink = level.time + FRAMETIME; } else { self->enemy = NULL; } } }
gentity_t *SelectNearestDeathmatchSpawnPoint( bvec3_t from ) { gentity_t *spot; bvec3_t delta; bfixed dist, nearestDist; gentity_t *nearestSpot; nearestDist = BFIXED(999999,0); nearestSpot = NULL; spot = NULL; while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { VectorSubtract( spot->s.origin, from, delta ); dist = FIXED_VEC3LEN( delta ); if ( dist < nearestDist ) { nearestDist = dist; nearestSpot = spot; } } return nearestSpot; }
void BotFrame( void ) { gedict_t *te, *oself; oself = self; for ( te = world; ( te = trap_find( te, FOFS( s.v.classname ), "player" ) ); ) { if ( te->has_disconnected ) continue; if ( !te->isBot ) continue; self = te; if( !tf_data.enable_bot ) { botDisconnect( self ); continue; } self->old_button0 = self->s.v.button0; self->old_button2 = self->s.v.button2; self->old_keys = self->keys; self->s.v.button0 = 0; self->s.v.button2 = 0; self->s.v.impulse = 0; self->keys = 0; if( self->team_no && self->team_no != 1) { G_bprint(3,"%s: i'm dont know how to play for team 2\n",self->s.v.netname); botDisconnect(self); continue; } Bot_AI( ); Bot_CL_KeyMove( ); } self = oself; }
/* ============================== G_UseTargets "activator" should be set to the entity that initiated the firing. Search for (string)targetname in all entities that match (string)self.target and call their .use function ============================== */ void G_UseTargets( gentity_t *ent, gentity_t *activator ) { gentity_t *t; if ( ent->targetShaderName && ent->targetShaderNewName ) { float f = level.time * 0.001; AddRemap( ent->targetShaderName, ent->targetShaderNewName, f ); trap_SetConfigstring( CS_SHADERSTATE, BuildShaderStateConfig() ); } if ( !ent->target ) { return; } t = NULL; while ( ( t = G_Find( t, FOFS( targetname ), ent->target ) ) != NULL ) { if ( t == ent ) { G_Printf( "WARNING: Entity used itself.\n" ); } else { if ( t->use ) { t->use( t, ent, activator ); } } if ( !ent->inuse ) { G_Printf( "entity was removed while using targets\n" ); return; } } }
void Eng_DispUnload( ) { gedict_t *disp; float power; for ( disp = world; (disp = trap_find( disp, FOFS( s.v.classname ), "building_dispenser" )); ) { disp->s.v.ammo_cells = disp->s.v.ammo_cells - 20; disp->s.v.ammo_rockets = disp->s.v.ammo_rockets - 15; if ( disp->s.v.ammo_rockets < 0 ) disp->s.v.ammo_rockets = 0; if ( disp->s.v.ammo_cells < 0 ) disp->s.v.ammo_cells = 0; self->hook_out = 0; power = ceil( 25 + disp->s.v.ammo_rockets * 1.5 + disp->s.v.ammo_cells ); if ( power > 250 ) power = 250; G_bprint( 2, "maximum detdispenser damage - %.0f\n", power ); return; } G_sprint( self, 2, "no disp\n" ); }
static void target_lightramp_use( edict_t *self, edict_t *other, edict_t *activator ) { if( !self->enemy ) { edict_t *e; // check all the targets e = NULL; while( 1 ) { e = G_Find( e, FOFS( targetname ), self->target ); if( !e ) break; if( Q_stricmp( e->classname, "light" ) ) { if( developer->integer ) { G_Printf( "%s at %s ", self->classname, vtos( self->s.origin ) ); G_Printf( "target %s (%s at %s) is not a light\n", self->target, e->classname, vtos( e->s.origin ) ); } } else { self->enemy = e; } } if( !self->enemy ) { if( developer->integer ) G_Printf( "%s target %s not found at %s\n", self->classname, self->target, vtos( self->s.origin ) ); G_FreeEdict( self ); return; } } self->timeStamp = level.time; target_lightramp_think( self ); }
gentity_t *SelectNearestDeathmatchSpawnPoint( vec3_t from ) { gentity_t *spot; vec3_t delta; float dist, nearestDist; gentity_t *nearestSpot; nearestDist = 999999; nearestSpot = NULL; spot = NULL; while ((spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) != NULL) { VectorSubtract( spot->s.origin, from, delta ); dist = VectorLength( delta ); if ( dist < nearestDist ) { nearestDist = dist; nearestSpot = spot; } } return nearestSpot; }
static void SP_FixCoopSpots(edict_t *self){ edict_t *spot; vec3_t d; spot = NULL; while(1){ spot = G_Find(spot, FOFS(classname), "info_player_start"); if(!spot) return; if(!spot->targetname) continue; VectorSubtract(self->s.origin, spot->s.origin, d); if(VectorLength(d) < 384){ if((!self->targetname) || Q_stricmp(self->targetname, spot->targetname) != 0){ // gi.dprintf("FixCoopSpots changed %s at %s targetname from %s to %s\n", self->classname, vtos(self->s.origin), self->targetname, spot->targetname); self->targetname = spot->targetname; } return; } } }
int ClassIsRestricted( int tno, int pc ) { int max, num = 0; gedict_t *te; if ( pc <= 0 || pc > 10 ) return 0; if ( !tno ) return 0; max = GetSVInfokeyInt( li_classrestricted[pc-1][0], li_classrestricted[pc-1][1], 0 ); if ( max > 0 ) { for ( te = world; ( te = trap_find( te, FOFS( s.v.classname ), "player" ) ); ) { if ( te->team_no == tno ) { if ( pc == 10 ) { if ( te->tfstate & TFSTATE_RANDOMPC ) num++; } else { if ( te->playerclass == pc || te->nextpc == pc ) { if ( !( te->tfstate & TFSTATE_RANDOMPC ) ) num++; } } } } if ( num >= max ) return 1; } if ( max == -1 ) return 1; return 0; }
static void drawhline(int x,int y,int w,GrxColor color) { int copr, pl; GR_int32u offs; GRX_ENTER(); copr = C_OPER(color); offs = FOFS(x,y,CURC->gc_line_offset); for (pl=0; pl < 3; ++pl) { if(DOCOLOR8(color,copr)) { GR_repl cval = freplicate_b(color); char *pp = &GRX_FRAME_MEMORY_PLANE(&CURC->gc_base_address,pl)[offs]; int ww = w; switch(copr) { case C_XOR: repfill_b_xor(pp,cval,ww); break; case C_OR: repfill_b_or( pp,cval,ww); break; case C_AND: repfill_b_and(pp,cval,ww); break; default: repfill_b( pp,cval,ww); break; } } color >>= 8; } GRX_LEAVE(); }
void EAVYSpawnTeamNearFlag(edict_t *flag) { edict_t *spot = NULL; float dist; vec3_t v; while(spot = G_Find (spot, FOFS(classname), "info_player_deathmatch")) { VectorSubtract (spot->s.origin, flag->s.origin, v); dist = VectorLength (v); if (EAVY_RESTRICTED_RADIUS > dist) { if (!strcmp(flag->classname, "item_flag_team1")) spot->classname = "info_player_team1"; if (!strcmp(flag->classname, "item_flag_team2")) spot->classname = "info_player_team2"; spot->svflags |= SVF_NOCLIENT; spot->solid = SOLID_NOT; ED_CallSpawn (spot); } } }
void target_laser_start(gentity_t *self) { gentity_t *ent; self->s.eType = ET_BEAM; if(self->target) { ent = G_Find(NULL, FOFS(targetname), self->target); if(!ent) { G_Printf("%s at %s: %s is a bad target\n", self->classname, vtos(self->s.origin), self->target); } self->enemy = ent; } else { G_SetMovedir(self->s.angles, self->movedir); } self->use = target_laser_use; self->think = target_laser_think; if(!self->damage) { self->damage = 1; } if(self->spawnflags & 1) { target_laser_on(self); } else { target_laser_off(self); } }
/* ================== FindIntermissionPoint This is also used for spectator spawns ================== */ void FindIntermissionPoint( void ) { gentity_t *ent, *target; vec3_t dir; // find the intermission spot ent = G_Find (NULL, FOFS(classname), "info_player_intermission"); if ( !ent ) { // the map creator forgot to put in an intermission point... SelectSpawnPoint ( vec3_origin, level.intermission_origin, level.intermission_angle, qfalse ); } else { VectorCopy (ent->s.origin, level.intermission_origin); VectorCopy (ent->s.angles, level.intermission_angle); // if it has a target, look towards it if ( ent->target ) { target = G_PickTarget( ent->target ); if ( target ) { VectorSubtract( target->s.origin, level.intermission_origin, dir ); vectoangles( dir, level.intermission_angle ); } } } }
/* =========== G_SelectInitialSpawnPoint Try to find a spawn point marked 'initial', otherwise use normal spawn selection. ============ */ gentity_t *G_SelectInitialSpawnPoint( vec3_t origin, vec3_t angles ) { gentity_t *spot; spot = NULL; while( ( spot = G_Find( spot, FOFS( classname ), "info_player_deathmatch" ) ) != NULL ) { if( spot->spawnflags & 1 ) break; } if( !spot || SpotWouldTelefrag( spot ) ) { return G_SelectSpawnPoint( vec3_origin, origin, angles ); } VectorCopy( spot->s.origin, origin ); origin[ 2 ] += 9; VectorCopy( spot->s.angles, angles ); return spot; }
static void target_teleporter_use( edict_t *self, edict_t *other, edict_t *activator ) { edict_t *dest; if( !G_PlayerCanTeleport( activator ) ) return; if( ( self->s.team != TEAM_SPECTATOR ) && ( self->s.team != activator->s.team ) ) return; if( self->spawnflags & 1 && activator->r.client->ps.pmove.pm_type != PM_SPECTATOR ) return; dest = G_Find( NULL, FOFS( targetname ), self->target ); if( !dest ) { if( developer->integer ) G_Printf( "Couldn't find destination.\n" ); return; } G_TeleportPlayer( activator, dest ); }
void target_lock_use(edict_t *self, edict_t *other, edict_t *activator) { char current[16]; memset(current, 0, 16); for (edict_t *e = self->teammaster; e; e = e->teamchain) { if (!e->count) continue; const int n = e->count - 1; current[n] = '0' + e->s.frame; } if (strcmp(current, self->key_message) == 0) { char *copy_message = self->message; self->message = NULL; G_UseTargets(self, activator); self->message = copy_message; } else { if (self->message) safe_centerprintf(activator, self->message); if (self->pathtarget) { edict_t *e = G_Find(NULL, FOFS(targetname), self->pathtarget); if (e) e->use(e, other, activator); } else { BeepBeep(activator); } } }
void shipboundary_touch( gentity_t *self, gentity_t *other, trace_t *trace ) { gentity_t *ent; if (!other || !other->inuse || !other->client || other->s.number < MAX_CLIENTS || !other->m_pVehicle) { //only let vehicles touch return; } if ( other->client->ps.hyperSpaceTime && level.time - other->client->ps.hyperSpaceTime < HYPERSPACE_TIME ) {//don't interfere with hyperspacing ships return; } ent = G_Find (NULL, FOFS(targetname), self->target); if (!ent || !ent->inuse) { //this is bad trap->Error(ERR_DROP, "trigger_shipboundary has invalid target '%s'\n", self->target); return; } if (!other->client->ps.m_iVehicleNum || other->m_pVehicle->m_iRemovedSurfaces) { //if a vehicle touches a boundary without a pilot in it or with parts missing, just blow the thing up G_Damage(other, other, other, NULL, other->client->ps.origin, 99999, DAMAGE_NO_PROTECTION, MOD_SUICIDE); return; } //make sure this sucker is linked so the prediction knows where to go trap->LinkEntity((sharedEntity_t *)ent); other->client->ps.vehTurnaroundIndex = ent->s.number; other->client->ps.vehTurnaroundTime = level.time + (self->genericValue1*2); //keep up the detailed checks for another 2 seconds self->genericValue7 = level.time + 2000; }
/* ================= G_ScriptAction_Trigger syntax: trigger <aiName/scriptName> <trigger> Calls the specified trigger for the given ai character or script entity ================= */ qboolean G_ScriptAction_Trigger( gentity_t *ent, char *params ) { gentity_t *trent; char *pString, name[MAX_QPATH], trigger[MAX_QPATH], *token; int oldId; // get the cast name pString = params; token = COM_ParseExt( &pString, qfalse ); Q_strncpyz( name, token, sizeof( name ) ); if ( !name[0] ) { G_Error( "G_Scripting: trigger must have a name and an identifier\n" ); } token = COM_ParseExt( &pString, qfalse ); Q_strncpyz( trigger, token, sizeof( trigger ) ); if ( !trigger[0] ) { G_Error( "G_Scripting: trigger must have a name and an identifier\n" ); } trent = AICast_FindEntityForName( name ); if ( trent ) { // we are triggering an AI //oldId = trent->scriptStatus.scriptId; AICast_ScriptEvent( AICast_GetCastState( trent->s.number ), "trigger", trigger ); return qtrue; } // look for an entity trent = G_Find( &g_entities[MAX_CLIENTS], FOFS( scriptName ), name ); if ( trent ) { oldId = trent->scriptStatus.scriptId; G_Script_ScriptEvent( trent, "trigger", trigger ); // if the script changed, return false so we don't muck with it's variables return ( ( trent != ent ) || ( oldId == trent->scriptStatus.scriptId ) ); } G_Error( "G_Scripting: trigger has unknown name: %s\n", name ); return qfalse; // shutup the compiler }
/* * Searches all active entities for the next one that holds * the matching string at fieldofs (use the FOFS() macro) in the structure. * * Searches beginning at the edict after from, or the beginning if NULL * NULL will be returned if the end of the list is reached. */ edict_t * G_PickTarget(char *targetname) { edict_t *ent = NULL; int num_choices = 0; edict_t *choice[MAXCHOICES]; if (!targetname) { gi.dprintf("G_PickTarget called with NULL targetname\n"); return NULL; } while (1) { ent = G_Find(ent, FOFS(targetname), targetname); if (!ent) { break; } choice[num_choices++] = ent; if (num_choices == MAXCHOICES) { break; } } if (!num_choices) { gi.dprintf("G_PickTarget: target %s not found\n", targetname); return NULL; } return choice[rand() % num_choices]; }
gentity_t *G_PickTarget( const char *targetname ) { gentity_t *ent = NULL; int num_choices = 0; gentity_t *choice[ MAXCHOICES ]; if ( !targetname ) { G_Printf( "G_PickTarget called with NULL targetname\n" ); return NULL; } while ( 1 ) { ent = G_Find( ent, FOFS( targetname ), targetname ); if ( !ent ) { break; } choice[ num_choices++ ] = ent; if ( num_choices == MAXCHOICES ) { break; } } if ( !num_choices ) { G_Printf( "G_PickTarget: target %s not found\n", targetname ); return NULL; } return choice[ rand() / ( RAND_MAX / num_choices + 1 ) ]; }
static void drawvline(int x,int y,int h,GrxColor color) { int copr, pl; GR_int32u offs; unsigned lwdt; GRX_ENTER(); copr = C_OPER(color); lwdt = CURC->gc_line_offset; offs = FOFS(x,y,lwdt); for (pl=0; pl < 3; ++pl) { if(DOCOLOR8(color,copr)) { char *pp = &GRX_FRAME_MEMORY_PLANE(&CURC->gc_base_address,pl)[offs]; int hh = h; switch(copr) { case C_XOR: colfill_b_xor(pp,lwdt,(GR_int8u)color,hh); break; case C_OR: colfill_b_or( pp,lwdt,(GR_int8u)color,hh); break; case C_AND: colfill_b_and(pp,lwdt,(GR_int8u)color,hh); break; default: colfill_b( pp,lwdt,(GR_int8u)color,hh); break; } } color >>= 8; } GRX_LEAVE(); }
void DestroyBuilding( gedict_t * eng, char *bld ) { gedict_t *te; gedict_t *oldself; float pos; for ( te = world; (te = trap_find( te, FOFS( s.v.classname ), bld )); ) { if ( te->real_owner == eng ) { pos = trap_pointcontents( PASSVEC3( te->s.v.origin ) ); if ( pos == CONTENT_SOLID || pos == CONTENT_SKY ) { oldself = self; self = eng; self->s.v.ammo_cells = self->s.v.ammo_cells + 100; bound_other_ammo( self ); W_SetCurrentAmmo( ); self = oldself; } if ( te->real_owner->building == te ) { if ( !te->real_owner->StatusBarSize ) CenterPrint( te->real_owner, "\n" ); else te->real_owner->StatusRefreshTime = g_globalvars.time + 0.1; te->real_owner->menu_count = MENU_REFRESH_RATE; te->real_owner->current_menu = MENU_DEFAULT; te->real_owner->building = world; } if( tg_data.tg_enabled ) te->has_sentry = 0; TF_T_Damage( te, world, world, 500, 0, 0 ); } } }
void smoke_init(gentity_t *ent) { ent->think = smoke_think; ent->nextthink = level.time + FRAMETIME; if (ent->target) { gentity_t *target; target = G_Find(NULL, FOFS(targetname), ent->target); if (target) { vec3_t vec; VectorSubtract(target->s.origin, ent->s.origin, vec); VectorCopy(vec, ent->s.origin2); } else { VectorSet(ent->s.origin2, 0, 0, 1); } } else { VectorSet(ent->s.origin2, 0, 0, 1); } if (ent->spawnflags & 4) { trap_LinkEntity(ent); } }
/* ============================== G_ToggleTargetsEnabled "activator" should be set to the entity that initiated the firing. Search for (string)targetname in all entities that match (string)self.target and toggle their FL_DISABLED flag ============================== */ void G_ToggleTargetsEnabled( gentity_t *ent, gentity_t *activator ) { gentity_t *t; if ( !ent ) { return; } if (ent->targetShaderName && ent->targetShaderNewName) { float f = level.time * 0.001; AddRemap(ent->targetShaderName, ent->targetShaderNewName, f); trap_SetConfigstring(CS_SHADERSTATE, BuildShaderStateConfig()); } if ( !ent->target ) { return; } t = NULL; while ( (t = G_Find (t, FOFS(targetname), ent->target)) != NULL ) { if ( t == ent ) { G_Printf ("WARNING: Entity targets itself.\n"); } else { G_Printf("entity found\n"); if ( ( ent->spawnflags & 4 ) ) t->flags |= FL_DISABLED; //always_disable spawnflag is set, so set the disabled bit else if ( ( ent->spawnflags & 8 ) ) t->flags &= ~FL_DISABLED; //always_enable spawnflag is set, so clear the disabled bit else t->flags ^= FL_DISABLED; //no spawnflag is set, so toggle } if ( !ent->inuse ) { G_Printf("entity was removed while using targets\n"); return; } } }
// target_lock_code reveals the lock combination to the target_lock specified in it's target field, or in global game data for crosslevel locks void lock_code_use(edict_t *self, edict_t *other, edict_t *activator) { char message[64]; if (self->spawnflags & 1) { if (!strlen(game.lock_code)) { gi.dprintf("Lock has not been properly initialized.\n"); return; } Com_sprintf(message, sizeof(message), "Lock combination is %s", game.lock_code); Do_Text_Display(activator, 0, message); const int len = strlen(game.lock_code); for (int i = 0; i < len; i++) game.lock_revealed |= 1 << i; } else { edict_t *lock = G_Find(NULL, FOFS(targetname), self->target); if (!lock) { gi.dprintf("Target of target_lock_code does not exist\n"); } else { Com_sprintf(message, sizeof(message), "Lock combination is %s", game.lock_code); Do_Text_Display(activator, 0, message); const int len = min(8, strlen(lock->key_message)); for (int i = 0; i < len; i++) game.lock_revealed |= 1<<i; } } }