Massassi Forums Logo

This is the static archive of the Massassi Forums. The forums are closed indefinitely. Thanks for all the memories!

You can also download Super Old Archived Message Boards from when Massassi first started.

"View" counts are as of the day the forums were archived, and will no longer increase.

ForumsCog Forum → Senarius Cog Help
Senarius Cog Help
2005-11-23, 1:45 PM #1
I am working on this cog right now but I have one bug that I haven't been able to work out.

The cog is supposed to activate a forcefield at the activation of a switch, heal and recharge the shields of the player that activated the switch, sleep while healing and the forcefield is on and then deactivating the the healing and the forcefield, with a delay afterward preventing the switch from being activated too soon. It works fine but if you activate the switch during the delay after it still heals you. I have tried to counter this with an if else statement regarding to the ajoinflags but it doesn't seem to work. I'm sorry if that didn't make sense. It would be great to have some tips. I am a beggining cogger so I don't know a way to counter this problem even though it should have a simple solution.

here it is (It is a modified version of 00_DUALFORCEFIELD.COG in the cog section of Massassi)
Code:
symbols

surface     switch                           desc=switch mask=0x408
surface     forcefield1                      desc=forcefld1 mask=0x408
surface     forcefield2                      desc=forcefld2 mask=0x408
surface     forcefield3                      desc=forcefld3 mask=0x408
surface     forcefield4                      desc=forcefld4 mask=0x408

thing       player                           local
thing       powerupspot

template    poweruptemp

flex        time_on=5.0                      desc=time_on
flex        delay=10                         desc=delay

int         field_is_on=0                    local
int         shields                          local

flex        maxDamage=10.0                   local
flex        minDamage=5.0                    local
flex        dmg=0.0                          local
int         ff_sound                         local
int         ffd_sound                        local
int         dummy                            local

sound       on_snd=set_hi2.wav               local
sound       off_snd=lgclick1.wav             local
sound       forcefield_snd=ForceFieldHum01.WAV  local
sound       ffdamage_snd=ForceFieldHit01.WAV local

flex        healspeed=.2
flex        healammount=1
flex        currenttime                      local
flex        oldtime1=0                       local
flex        oldtime2=0                       local
flex        oldtime3=0                       local
flex        oldtime4=0                       local
int         dodamage=0                       local

int         sourceRef=-1                     local
int         senderRef=-1                     local
int         lock_touched=0                   local

message     startup
message     activated
message     pulse
message     touched
message     timer

end

# ========================================================================================

code

startup:
   SetFaceGeoMode(forcefield1, 0);        // GEOMETRY_NONE    (don't draw the face)
   SetFaceGeoMode(forcefield2, 0);
   SetFaceGeoMode(forcefield3, 0);
   SetFaceGeoMode(forcefield4, 0);
   SetWallCel(switch, 1);

   Return;

# ........................................................................................
activated:
   
   if(GetAdjoinFlags(forcefield1) = 7)
         {
            setPulse(healspeed);
         }
   else
         {
            setPulse(0);
         }

   if(field_is_on == 0)
   {
      field_is_on = 1;

      // Center the sound on the switch
      ff_sound = PlaySoundPos(forcefield_snd, SurfaceCenter(forcefield1), 1.0, -1, -1, 0x41);

      // SetFaceGeoMode(forcefield1, 5);     // GEOMETRY_FULL     (draw the face)
      // SetFaceGeoMode(forcefield2, 5);
      // SetFaceGeoMode(forcefield3, 5);
      // SetFaceGeoMode(forcefield4, 5);

      // Switched to a solid material, this gives
      // MUCH more efficient translucency

      SetFaceGeoMode(forcefield1, 4);        // GEOMETRY_SOLID
      SetFaceGeoMode(forcefield2, 4);
      SetFaceGeoMode(forcefield3, 4);
      SetFaceGeoMode(forcefield4, 4);

      SetFaceLightMode(forcefield1, 0);      // LIGHTING_LIT      (light fully)
      SetFaceLightMode(forcefield2, 0);
      SetFaceLightMode(forcefield3, 0);
      SetFaceLightMode(forcefield4, 0);
      ClearAdjoinFlags(forcefield1, 2);      // Clear ADJOIN_MOVE (disallow passage)
      ClearAdjoinFlags(forcefield2, 2);
      ClearAdjoinFlags(forcefield3, 2);
      ClearAdjoinFlags(forcefield4, 2);
      SetSurfaceFlags(forcefield1,16384);    // Set SURFACE_MAGSEALED
      SetSurfaceFlags(forcefield2,16384);
      SetSurfaceFlags(forcefield3,16384);
      SetSurfaceFlags(forcefield4,16384);
      SetWallCel(switch, 0);
      dummy = PlaySoundPos(on_snd, SurfaceCenter(switch), 0.8, 5.0, 10.0, 0);

      SetTimer(time_on);                     // Let the force field active

pulse:

   player = GetSenderRef();

         if((GetThingHealth(player) > 0) && (GetThingHealth(player) < 100))
         {
            HealThing(player, healammount);
         }

         if((GetThingHealth(player) == 100) && (GetInv(player,60) < 200))
         {
            ChangeInv(player, 60, healammount);
         }            

Return;

//........................................................................................

timer:

      SetPulse(0);                        

      SetFaceGeoMode(forcefield1, 0);        // GEOMETRY_NONE    (don't draw the face)
      SetFaceGeoMode(forcefield2, 0);
      SetFaceGeoMode(forcefield3, 0);
      SetFaceGeoMode(forcefield4, 0);
      SetFaceLightMode(forcefield1, 3);      // LIGHTING_GOURAUD (normal lighting)
      SetFaceLightMode(forcefield2, 3);
      SetFaceLightMode(forcefield3, 3);
      SetFaceLightMode(forcefield4, 3);
      SetAdjoinFlags(forcefield1, 2);        // Set ADJOIN_MOVE  (allow passage)
      SetAdjoinFlags(forcefield2, 2);
      SetAdjoinFlags(forcefield3, 2);
      SetAdjoinFlags(forcefield4, 2);
      ClearSurfaceFlags(forcefield1,16384);  // Clear SURFACE_MAGSEALED
      ClearSurfaceFlags(forcefield2,16384);
      ClearSurfaceFlags(forcefield3,16384);
      ClearSurfaceFlags(forcefield4,16384);

      StopSound(ff_sound, 0);

      Sleep(delay);                          // Delay before the force field is usable again

      SetWallCel(switch, 1);
      dummy = PlaySoundPos(off_snd, SurfaceCenter(switch), 0.8, 5.0, 10.0, 0);

      field_is_on = 0;
   }

   Return;

// ........................................................................................
//
// touched: is very framerate dependant, so we'll apply damage only
// when 0.5 second have elapsed. Note that we have to remember the
// time for each of the forcefields.

touched:
   if(GetSenderRef() == switch) Return;

   if(lock_touched == 1) Return;
   lock_touched = 1;

   senderRef = GetSenderRef();
   sourceRef = GetSourceRef();

   if (field_is_on == 1)
   {
      currenttime = GetLevelTime();
      dodamage = 0;

      if((senderRef == forcefield1) && (currenttime > oldtime1 + 0.50))
      {
         oldtime1 = currenttime;
         dodamage = 1;
      }
      else
      if((senderRef == forcefield2) && (currenttime > oldtime2 + 0.50))
      {
         oldtime2 = currenttime;
         dodamage = 1;
      }
      else
      if((senderRef == forcefield3) && (currenttime > oldtime3 + 0.50))
      {
         oldtime3 = currenttime;
         dodamage = 1;
      }
      else
      if((senderRef == forcefield4) && (currenttime > oldtime4 + 0.50))
      {
         oldtime4 = currenttime;
         dodamage = 1;
      }

      if(dodamage == 1)
      {
         dummy  = PlaySoundPos(ffdamage_snd, GetSurfaceCenter(senderRef), 1.0, -1, -1, 0);
         dummy  = DamageThing(sourceRef, 10, 0x2, sourceRef);
      }
   }

   lock_touched = 0;

   Return;

end
If curiosity killed the cat then perhaps Curious George killed the cat.
But Cat's do have nine lives so who knows?
2005-11-23, 6:30 PM #2
You're missing return statements and an ending curly bracket. Besides that, there's a lot of small problems. Parsec caught most of them, and the DM is there for the rest. So RTDMAUP. :p

Anyway, I fixed everything I could see. Can't say for sure it'll work without testing, but here goes: ;)

Code:
#==============================================================#
symbols

surface   switch                               mask=0x408
surface   forcefield0                          mask=0x408
surface   forcefield1                          mask=0x408
surface   forcefield2                          mask=0x408
surface   forcefield3                          mask=0x408

int       numFields
int       field_is_on=0                        local
int       ff_sound                             local
int       i                                    local

thing     player                               local

flex      time_on=5
flex      delay=10
flex      healspeed=0.2
flex      healammount=1
flex      oldtime0=0                           local
flex      oldtime1=0                           local
flex      oldtime2=0                           local
flex      oldtime3=0                           local

sound     on_snd=set_hi2.wav                   local
sound     off_snd=lgclick1.wav                 local
sound     forcefield_snd=ForceFieldHum01.WAV   local
sound     ffdamage_snd=ForceFieldHit01.WAV     local

message   startup
message   activated
message   pulse
message   touched
message   timer

end
#==============================================================#
code
#------------------------------------------------------
startup:
	for(i = 0; i < numFields; i = i + 1) SetFaceGeoMode(forcefield0, 0);
	SetWallCel(switch, 1);

Return;
#------------------------------------------------------
activated:
	if(field_is_on) Return;
	field_is_on = 1;
	player = GetSourceRef();
	SetPulse(healspeed);
	// sound played only at field 0.
	ff_sound = PlaySoundPos(forcefield_snd, SurfaceCenter(forcefield0), 1, -1, -1, 0x1);
	for(i = 0; i < numFields; i = i + 1)
	{
		SetFaceGeoMode(forcefield0, 4);
		SetFaceLightMode(forcefield0, 0);
		ClearAdjoinFlags(forcefield0, 2);
		SetSurfaceFlags(forcefield0, 16384);
	}
	SetWallCel(switch, 0);
	PlaySoundPos(on_snd, SurfaceCenter(switch), 0.8, 5, 10, 0x0);
	SetTimer(time_on);

Return;
#------------------------------------------------------
pulse:
	if(GetThingHealth(player) > 0 && GetThingHealth(player) < 100) HealThing(player, healammount);
	else if(GetThingHealth(player) == 100 && GetInv(player, 60) < 200) ChangeInv(player, 60, healammount);

Return;
#------------------------------------------------------
timer:
	SetPulse(0);
	for(i = 0; i < numFields; i = i + 1)
	{
		SetFaceGeoMode(forcefield0, 0);
		SetFaceLightMode(forcefield0, 3);
		SetAdjoinFlags(forcefield0, 2);
		ClearSurfaceFlags(forcefield0, 16384);
	}
	StopSound(ff_sound, 0);
	Sleep(delay);
	SetWallCel(switch, 1);
	PlaySoundPos(off_snd, SurfaceCenter(switch), 0.8, 5, 10, 0x0);
	field_is_on = 0;

Return;
#------------------------------------------------------
touched:
	if(GetSenderRef() == switch || !field_is_on) Return;
	for(i = 0; i < numFields; i = i + 1)
	{
		if(GetSenderRef() == forcefield0 && GetLevelTime() > oldtime0 + 0.5)
		{
			oldtime0 = GetLevelTime();
			PlaySoundPos(ffdamage_snd, GetSurfaceCenter(forcefield0), 1, -1, -1, 0x0);
			DamageThing(GetSourceRef(), 10, 0x2, GetSourceRef());
		}
	}

Return;
#------------------------------------------------------
end
Historians are the most powerful and dangerous members of any society. They must be watched carefully... They can spoil everything. - Nikita Khrushchev.
Kill one man, and you are a murderer. Kill millions of men, and you are a conqueror. Kill them all, and you are a god. - Jean Rostand.
2005-11-24, 4:26 PM #3
Thanks for the help Centrist! It works fine now. I haven't tested it for sync issues yet but it should be fine. I have another forcefield cog that Zagibu wrote for me but it has sync issues. I would be extremely greatful if I could get more help with that cog since it controls a good deal of the game flow in my level. I am depending on this cog's completion for my submission to the JK hub level contest.

This is a cog for multiplayer that will control force fields on 24 surfaces. The force fields will be on all the time unless someone enters one of six sectors. When someone is in one of those six sectors the force fields will deactivate. It also animates the forcefield texture. As of now the cog works but it has sync issues (some surfaces will not be active when they are supposed to be or some will be active when they aren't supposed to be; this only occurs on guest computers). Zagibu wrote this cog for me but didn't want to deal with the sync problems. It would be a great help in getting this project along if anyone could get this debugged and working correctly.

Code:
# complex_forcefield.cog
# 
# Controls 24 forcefield surfaces using 6 sectors as triggers
#
# Created by zagibu@gmx.ch

flags=0x240

symbols

material	anim_material
flex		anim_speed=5

flex		damage_amount=10
int		damage_type=2
sound		damage_sound=forcefieldhit01.wav

sector		trigger_sec1=-1
sector		trigger_sec2=-1
sector		trigger_sec3=-1
sector		trigger_sec4=-1
sector		trigger_sec5=-1
sector		trigger_sec6=-1

surface		force_surf01=-1				mask=0x400	linkid=102
surface		force_surf02=-1				mask=0x400	linkid=102
surface		force_surf03=-1				mask=0x400	linkid=102
surface		force_surf04=-1				mask=0x400	linkid=102
surface		force_surf05=-1				mask=0x400	linkid=102
surface		force_surf06=-1				mask=0x400	linkid=102
surface		force_surf07=-1				mask=0x400	linkid=102
surface		force_surf08=-1				mask=0x400	linkid=102
surface		force_surf09=-1				mask=0x400	linkid=102
surface		force_surf10=-1				mask=0x400	linkid=102
surface		force_surf11=-1				mask=0x400	linkid=102
surface		force_surf12=-1				mask=0x400	linkid=102
surface		force_surf13=-1				mask=0x400	linkid=102
surface		force_surf14=-1				mask=0x400	linkid=102
surface		force_surf15=-1				mask=0x400	linkid=102
surface		force_surf16=-1				mask=0x400	linkid=102
surface		force_surf17=-1				mask=0x400	linkid=102
surface		force_surf18=-1				mask=0x400	linkid=102
surface		force_surf19=-1				mask=0x400	linkid=102
surface		force_surf20=-1				mask=0x400	linkid=102
surface		force_surf21=-1				mask=0x400	linkid=102
surface		force_surf22=-1				mask=0x400	linkid=102

surface		force_surf23=-1				mask=0x400	linkid=102
surface		force_surf24=-1				mask=0x400	linkid=102

int		empty					local
int		lastempty				local
int		i					local
int		j					local

message		startup
message		touched
message		pulse
message		trigger

end

# ==================================================  ======================================

code

startup:
	// Wait a short while to make sure level is completely loaded
	Sleep(1);

	// Initialize variables
	empty = 1;
	lastempty = 1;

	// Start the material animation
	MaterialAnim(anim_material, anim_speed, 0x1);

	// Start the sector check pulse
	SetPulse(0.2);

	Return;

# ..................................................  ......................................

touched:
	// Check if the message was sent from an interesting surface
	if(GetSenderId() != 102)
		Return;

	// Send damage player trigger
	SendTrigger(-1, 13373, GetSourceRef(), -1, -1, -1);


	Return;
	
# ..................................................  ......................................

pulse:
	// Initialize emptycheck variable
	empty = 1;

	// Loop through all players
	for(i = 0; i < GetNumPlayers(); i = i + 1)
	{
		// Loop through all trigger sectors
		for(j = 0; j < 6; j = j + 1)
		{
			if(GetThingSector(GetPlayerThing(i)) == trigger_sec1[j])
				empty = 0;
		}
	}

	// Check if empty state has changed at all
	if(empty == lastempty)
		Return;

	// Are the sectors empty? Then activate the forcefields
	if(empty)
		SendTrigger(-1, 13771, -1, -1, -1, -1);
	// Sectors are not empty. So deactivate the forcefields
	else
		SendTrigger(-1, 13772, -1, -1, -1, -1);

	// Update trailing variable
	lastempty = empty;

	Return;

# ..................................................  ......................................

trigger:
	// Activation trigger
	if(GetSourceRef() == 13771)
	{
		// Loop through all surfaces
		for(i = 0; i < 24; i = i + 1)
		{
			if(force_surf01 != -1)
			{
				// Render the surfaces visible
				SetFaceGeoMode(force_surf01, 4);
				// Make the surfaces solid by clearing the "passable" flag
				ClearAdjoinFlags(force_surf01, 2);
			}
		}
	}

	// Deactivation trigger
	if(GetSourceRef() == 13772)
	{
		// Loop through all surfaces
		for(i = 0; i < 24; i = i + 1)
		{
			if(force_surf01 != -1)
			{
				// Render the surfaces invisible
				SetFaceGeoMode(force_surf01, 0);
				// Make the surfaces passable
				SetAdjoinFlags(force_surf01, 2);
			}
		}
	}

	// Player damage trigger
	if(GetSourceRef() == 13773)
	{
		// Damage the player
		if(isServer())
			DamageThing(GetParam(0), damage_amount, damage_type, -1);

		// Play sparkling sound
		PlaySoundThing(damage_sound, GetParam(0), 1, 0, 10, 0);
	}

	Return;
end
If curiosity killed the cat then perhaps Curious George killed the cat.
But Cat's do have nine lives so who knows?
2005-11-24, 5:06 PM #4
Well first of all, the touched handler is sending the wrong trigger id.

This cog is using the 0x200 and 0x40 cog flags to make it run locally without syncing, but it's using triggers that are sent to all computers. The verbs used here would probably sync their effects just fine, and it would be better to have the server control the surface activation. So I suggest turning this into a server-side cog which would work a lot like the first cog.

Can you send me a copy of your project so I can test the cog?
Historians are the most powerful and dangerous members of any society. They must be watched carefully... They can spoil everything. - Nikita Khrushchev.
Kill one man, and you are a murderer. Kill millions of men, and you are a conqueror. Kill them all, and you are a god. - Jean Rostand.
2005-11-24, 11:00 PM #5
That sounds great! I have posted another beta version on the Hub so here is a link that will bring you to the download. I may have changed the cog in the level from what I've posted here. Just use the one in the level though. It should be fine. Thanks for the help again. I really have no idea what I'm doing when it comes to cog but I'm trying. It's been hard not being able to get anyone to help me with this. I'm really grateful that you're willing to help me.
If curiosity killed the cat then perhaps Curious George killed the cat.
But Cat's do have nine lives so who knows?
2005-11-25, 4:21 PM #6
The problem with the second cog was probably caused because every machine was running a pulse that was sending a trigger to everyone else. It wouldn't cause a problem right away, but it would with a little latency.

Also, JK can't sync the changes made to 24 surfaces all at once - a few are left out. That's why I couldn't use a server-side cog like I wanted to. Here's the finished cog:

Code:
# complex_forcefield.cog
#
# Controls 24 forcefield surfaces using 6 sectors as triggers. This cog runs locally without syncing.
# The pulse is run on the server and triggers are used to sync effects.
#
# Created by zagibu@gmx.ch and Centrist
#==============================================================#
flags=0x240
#==============================================================#
symbols

material  animMaterial

flex      animSpeed=8                       local

int       inside                            local
int       active=1                          local
int       i                                 local
int       j                                 local
int       id                                local
int       numTriggerSectors=0
int       numFieldSurfaces=0

sound     damageSound=forcefieldhit01.wav   local
sound     humSound=ForceFieldHum01.wav      local

sector    triggerSector0=-1                 nolink
sector    triggerSector1=-1                 nolink
sector    triggerSector2=-1                 nolink
sector    triggerSector3=-1                 nolink
sector    triggerSector4=-1                 nolink
sector    triggerSector5=-1                 nolink

surface   soundSurface=-1                   nolink
surface   fieldSurface00=-1                 mask=0x400
surface   fieldSurface01=-1                 mask=0x400
surface   fieldSurface02=-1                 mask=0x400
surface   fieldSurface03=-1                 mask=0x400
surface   fieldSurface04=-1                 mask=0x400
surface   fieldSurface05=-1                 mask=0x400
surface   fieldSurface06=-1                 mask=0x400
surface   fieldSurface07=-1                 mask=0x400
surface   fieldSurface08=-1                 mask=0x400
surface   fieldSurface09=-1                 mask=0x400
surface   fieldSurface10=-1                 mask=0x400
surface   fieldSurface11=-1                 mask=0x400
surface   fieldSurface12=-1                 mask=0x400
surface   fieldSurface13=-1                 mask=0x400
surface   fieldSurface14=-1                 mask=0x400
surface   fieldSurface15=-1                 mask=0x400
surface   fieldSurface16=-1                 mask=0x400
surface   fieldSurface17=-1                 mask=0x400
surface   fieldSurface18=-1                 mask=0x400
surface   fieldSurface19=-1                 mask=0x400
surface   fieldSurface20=-1                 mask=0x400
surface   fieldSurface21=-1                 mask=0x400
surface   fieldSurface22=-1                 mask=0x400
surface   fieldSurface23=-1                 mask=0x400

message   startup
message   touched
message   pulse
message   trigger

end
#==============================================================#
code
#------------------------------------------------------
startup:
	// activate the forcefields, start the mat anim, and start pulsing on server. done locally on all comps.
	call activate_field;
	MaterialAnim(animMaterial, animSpeed, 0x1);
	if(IsServer()) SetPulse(0.2);

Return;
#------------------------------------------------------
touched:
	// damage the player and play a damage sound. done locally on one comp.
	// trigger sent so all players will hear the sound.
	DamageThing(GetSourceRef(), 10, 2, GetSourceRef());
	SendTrigger(-1, 1339, GetSourceRef(), 0, 0, 0);

Return;
#------------------------------------------------------
pulse:
	// find out if a player is inside a trigger sector. server only.
	inside = 0;
	for(i = 0; i < GetNumPlayers() && !inside; i = i + 1)
		for(j = 0; j < numTriggerSectors && !inside; j = j + 1)
			if(GetThingSector(GetPlayerThing(i)) == triggerSector0[j]) inside = 1;

	// activate or deactivate the fields if we need to.
	if(!inside && !active) SendTrigger(-1, 1337, 0, 0, 0, 0);
	else if(inside && active) SendTrigger(-1, 1338, 0, 0, 0, 0);

Return;
#------------------------------------------------------
trigger:
	// handle triggers. done locally on all comps.
	if(GetSourceRef() == 1337) call activate_field;
	else if(GetSourceRef() == 1338) call deactivate_field;
	else if(GetSourceRef() == 1339) PlaySoundThing(damageSound, GetParam(0), 1, 0, 10, 0x0);

Return;
#------------------------------------------------------
activate_field:
	// make the fields visible and play the hum sound. done locally on all comps.
	active = 1;
	id = PlaySoundPos(humSound, GetSurfaceCenter(soundSurface), 1.5, 2, 10, 0x1);
	for(i = 0; i < numFieldSurfaces && fieldSurface00 != -1; i = i + 1)
	{
		SetFaceGeoMode(fieldSurface00, 4);
		ClearAdjoinFlags(fieldSurface00, 2);
	}

Return;
#------------------------------------------------------
deactivate_field:
	// make the fields invisible and stop the hum sound. done locally on all comps.
	active = 0;
	StopSound(id, 0);
	for(i = 0; i < numFieldSurfaces && fieldSurface00 != -1; i = i + 1)
	{
		SetFaceGeoMode(fieldSurface00, 0);
		SetAdjoinFlags(fieldSurface00, 2);
	}

Return;
#------------------------------------------------------
end


Everything is well-commented, and it worked fine when I tested it. :)

I did notice a problem with the other force fields, though. It looked like the dualforcefield cog didn't have the right surfaces passed to it. The result was a field that was only visible from one side.
Historians are the most powerful and dangerous members of any society. They must be watched carefully... They can spoil everything. - Nikita Khrushchev.
Kill one man, and you are a murderer. Kill millions of men, and you are a conqueror. Kill them all, and you are a god. - Jean Rostand.
2005-11-26, 9:39 PM #7
Thank's for the help! From what I can tell, they both work fine but I probably do need to set the other surface of the forcefield right. I would know for sure but I haven't had a chance to test it in multiplayer because my internet is acting strangely. I'll try again tommorow and post back on it.
If curiosity killed the cat then perhaps Curious George killed the cat.
But Cat's do have nine lives so who knows?
2005-11-27, 9:53 AM #8
Both cogs worked on the server but neither worked completely on a guest computer. The dualforcefield cog worked except that only the host could be healed when activating the switch. The complex forcefield cog just didn't turn any of the forcefields off on the guest computer when the host was in one of the trigger sectors. I'm very appreciative of your help. It would be great if you could help me work out these last two bugs so I can get my level fully functional.
If curiosity killed the cat then perhaps Curious George killed the cat.
But Cat's do have nine lives so who knows?

↑ Up to the top!